diff options
54 files changed, 3425 insertions, 684 deletions
diff --git a/org.eclipse.jgit.ant.test/.settings/org.eclipse.jdt.ui.prefs b/org.eclipse.jgit.ant.test/.settings/org.eclipse.jdt.ui.prefs index c336cce6ed..80d38d9c44 100644 --- a/org.eclipse.jgit.ant.test/.settings/org.eclipse.jdt.ui.prefs +++ b/org.eclipse.jgit.ant.test/.settings/org.eclipse.jdt.ui.prefs @@ -9,21 +9,23 @@ org.eclipse.jdt.ui.staticondemandthreshold=99 org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8"?><templates/> sp_cleanup.add_default_serial_version_id=true sp_cleanup.add_generated_serial_version_id=false -sp_cleanup.add_missing_annotations=false +sp_cleanup.add_missing_annotations=true sp_cleanup.add_missing_deprecated_annotations=true sp_cleanup.add_missing_methods=false sp_cleanup.add_missing_nls_tags=false sp_cleanup.add_missing_override_annotations=true -sp_cleanup.add_missing_override_annotations_interface_methods=false +sp_cleanup.add_missing_override_annotations_interface_methods=true sp_cleanup.add_serial_version_id=false sp_cleanup.always_use_blocks=true sp_cleanup.always_use_parentheses_in_expressions=false sp_cleanup.always_use_this_for_non_static_field_access=false sp_cleanup.always_use_this_for_non_static_method_access=false +sp_cleanup.convert_functional_interfaces=false sp_cleanup.convert_to_enhanced_for_loop=false sp_cleanup.correct_indentation=false sp_cleanup.format_source_code=true sp_cleanup.format_source_code_changes_only=true +sp_cleanup.insert_inferred_type_arguments=false sp_cleanup.make_local_variable_final=false sp_cleanup.make_parameters_final=false sp_cleanup.make_private_fields_final=true @@ -32,19 +34,20 @@ sp_cleanup.make_variable_declarations_final=false sp_cleanup.never_use_blocks=false sp_cleanup.never_use_parentheses_in_expressions=true sp_cleanup.on_save_use_additional_actions=true -sp_cleanup.organize_imports=false +sp_cleanup.organize_imports=true sp_cleanup.qualify_static_field_accesses_with_declaring_class=false sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true sp_cleanup.qualify_static_member_accesses_with_declaring_class=false sp_cleanup.qualify_static_method_accesses_with_declaring_class=false sp_cleanup.remove_private_constructors=true +sp_cleanup.remove_redundant_type_arguments=true sp_cleanup.remove_trailing_whitespaces=true sp_cleanup.remove_trailing_whitespaces_all=true sp_cleanup.remove_trailing_whitespaces_ignore_empty=false -sp_cleanup.remove_unnecessary_casts=false -sp_cleanup.remove_unnecessary_nls_tags=false -sp_cleanup.remove_unused_imports=false +sp_cleanup.remove_unnecessary_casts=true +sp_cleanup.remove_unnecessary_nls_tags=true +sp_cleanup.remove_unused_imports=true sp_cleanup.remove_unused_local_variables=false sp_cleanup.remove_unused_private_fields=true sp_cleanup.remove_unused_private_members=false @@ -52,8 +55,10 @@ sp_cleanup.remove_unused_private_methods=true sp_cleanup.remove_unused_private_types=true sp_cleanup.sort_members=false sp_cleanup.sort_members_all=false -sp_cleanup.use_blocks=false +sp_cleanup.use_anonymous_class_creation=false +sp_cleanup.use_blocks=true sp_cleanup.use_blocks_only_for_return_and_throw=false +sp_cleanup.use_lambda=false sp_cleanup.use_parentheses_in_expressions=false sp_cleanup.use_this_for_non_static_field_access=false sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true diff --git a/org.eclipse.jgit.ant/.settings/org.eclipse.jdt.ui.prefs b/org.eclipse.jgit.ant/.settings/org.eclipse.jdt.ui.prefs index c336cce6ed..80d38d9c44 100644 --- a/org.eclipse.jgit.ant/.settings/org.eclipse.jdt.ui.prefs +++ b/org.eclipse.jgit.ant/.settings/org.eclipse.jdt.ui.prefs @@ -9,21 +9,23 @@ org.eclipse.jdt.ui.staticondemandthreshold=99 org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8"?><templates/> sp_cleanup.add_default_serial_version_id=true sp_cleanup.add_generated_serial_version_id=false -sp_cleanup.add_missing_annotations=false +sp_cleanup.add_missing_annotations=true sp_cleanup.add_missing_deprecated_annotations=true sp_cleanup.add_missing_methods=false sp_cleanup.add_missing_nls_tags=false sp_cleanup.add_missing_override_annotations=true -sp_cleanup.add_missing_override_annotations_interface_methods=false +sp_cleanup.add_missing_override_annotations_interface_methods=true sp_cleanup.add_serial_version_id=false sp_cleanup.always_use_blocks=true sp_cleanup.always_use_parentheses_in_expressions=false sp_cleanup.always_use_this_for_non_static_field_access=false sp_cleanup.always_use_this_for_non_static_method_access=false +sp_cleanup.convert_functional_interfaces=false sp_cleanup.convert_to_enhanced_for_loop=false sp_cleanup.correct_indentation=false sp_cleanup.format_source_code=true sp_cleanup.format_source_code_changes_only=true +sp_cleanup.insert_inferred_type_arguments=false sp_cleanup.make_local_variable_final=false sp_cleanup.make_parameters_final=false sp_cleanup.make_private_fields_final=true @@ -32,19 +34,20 @@ sp_cleanup.make_variable_declarations_final=false sp_cleanup.never_use_blocks=false sp_cleanup.never_use_parentheses_in_expressions=true sp_cleanup.on_save_use_additional_actions=true -sp_cleanup.organize_imports=false +sp_cleanup.organize_imports=true sp_cleanup.qualify_static_field_accesses_with_declaring_class=false sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true sp_cleanup.qualify_static_member_accesses_with_declaring_class=false sp_cleanup.qualify_static_method_accesses_with_declaring_class=false sp_cleanup.remove_private_constructors=true +sp_cleanup.remove_redundant_type_arguments=true sp_cleanup.remove_trailing_whitespaces=true sp_cleanup.remove_trailing_whitespaces_all=true sp_cleanup.remove_trailing_whitespaces_ignore_empty=false -sp_cleanup.remove_unnecessary_casts=false -sp_cleanup.remove_unnecessary_nls_tags=false -sp_cleanup.remove_unused_imports=false +sp_cleanup.remove_unnecessary_casts=true +sp_cleanup.remove_unnecessary_nls_tags=true +sp_cleanup.remove_unused_imports=true sp_cleanup.remove_unused_local_variables=false sp_cleanup.remove_unused_private_fields=true sp_cleanup.remove_unused_private_members=false @@ -52,8 +55,10 @@ sp_cleanup.remove_unused_private_methods=true sp_cleanup.remove_unused_private_types=true sp_cleanup.sort_members=false sp_cleanup.sort_members_all=false -sp_cleanup.use_blocks=false +sp_cleanup.use_anonymous_class_creation=false +sp_cleanup.use_blocks=true sp_cleanup.use_blocks_only_for_return_and_throw=false +sp_cleanup.use_lambda=false sp_cleanup.use_parentheses_in_expressions=false sp_cleanup.use_this_for_non_static_field_access=false sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true diff --git a/org.eclipse.jgit.archive/.settings/org.eclipse.jdt.ui.prefs b/org.eclipse.jgit.archive/.settings/org.eclipse.jdt.ui.prefs index c336cce6ed..80d38d9c44 100644 --- a/org.eclipse.jgit.archive/.settings/org.eclipse.jdt.ui.prefs +++ b/org.eclipse.jgit.archive/.settings/org.eclipse.jdt.ui.prefs @@ -9,21 +9,23 @@ org.eclipse.jdt.ui.staticondemandthreshold=99 org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8"?><templates/> sp_cleanup.add_default_serial_version_id=true sp_cleanup.add_generated_serial_version_id=false -sp_cleanup.add_missing_annotations=false +sp_cleanup.add_missing_annotations=true sp_cleanup.add_missing_deprecated_annotations=true sp_cleanup.add_missing_methods=false sp_cleanup.add_missing_nls_tags=false sp_cleanup.add_missing_override_annotations=true -sp_cleanup.add_missing_override_annotations_interface_methods=false +sp_cleanup.add_missing_override_annotations_interface_methods=true sp_cleanup.add_serial_version_id=false sp_cleanup.always_use_blocks=true sp_cleanup.always_use_parentheses_in_expressions=false sp_cleanup.always_use_this_for_non_static_field_access=false sp_cleanup.always_use_this_for_non_static_method_access=false +sp_cleanup.convert_functional_interfaces=false sp_cleanup.convert_to_enhanced_for_loop=false sp_cleanup.correct_indentation=false sp_cleanup.format_source_code=true sp_cleanup.format_source_code_changes_only=true +sp_cleanup.insert_inferred_type_arguments=false sp_cleanup.make_local_variable_final=false sp_cleanup.make_parameters_final=false sp_cleanup.make_private_fields_final=true @@ -32,19 +34,20 @@ sp_cleanup.make_variable_declarations_final=false sp_cleanup.never_use_blocks=false sp_cleanup.never_use_parentheses_in_expressions=true sp_cleanup.on_save_use_additional_actions=true -sp_cleanup.organize_imports=false +sp_cleanup.organize_imports=true sp_cleanup.qualify_static_field_accesses_with_declaring_class=false sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true sp_cleanup.qualify_static_member_accesses_with_declaring_class=false sp_cleanup.qualify_static_method_accesses_with_declaring_class=false sp_cleanup.remove_private_constructors=true +sp_cleanup.remove_redundant_type_arguments=true sp_cleanup.remove_trailing_whitespaces=true sp_cleanup.remove_trailing_whitespaces_all=true sp_cleanup.remove_trailing_whitespaces_ignore_empty=false -sp_cleanup.remove_unnecessary_casts=false -sp_cleanup.remove_unnecessary_nls_tags=false -sp_cleanup.remove_unused_imports=false +sp_cleanup.remove_unnecessary_casts=true +sp_cleanup.remove_unnecessary_nls_tags=true +sp_cleanup.remove_unused_imports=true sp_cleanup.remove_unused_local_variables=false sp_cleanup.remove_unused_private_fields=true sp_cleanup.remove_unused_private_members=false @@ -52,8 +55,10 @@ sp_cleanup.remove_unused_private_methods=true sp_cleanup.remove_unused_private_types=true sp_cleanup.sort_members=false sp_cleanup.sort_members_all=false -sp_cleanup.use_blocks=false +sp_cleanup.use_anonymous_class_creation=false +sp_cleanup.use_blocks=true sp_cleanup.use_blocks_only_for_return_and_throw=false +sp_cleanup.use_lambda=false sp_cleanup.use_parentheses_in_expressions=false sp_cleanup.use_this_for_non_static_field_access=false sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true diff --git a/org.eclipse.jgit.http.apache/.settings/org.eclipse.jdt.ui.prefs b/org.eclipse.jgit.http.apache/.settings/org.eclipse.jdt.ui.prefs index c336cce6ed..80d38d9c44 100644 --- a/org.eclipse.jgit.http.apache/.settings/org.eclipse.jdt.ui.prefs +++ b/org.eclipse.jgit.http.apache/.settings/org.eclipse.jdt.ui.prefs @@ -9,21 +9,23 @@ org.eclipse.jdt.ui.staticondemandthreshold=99 org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8"?><templates/> sp_cleanup.add_default_serial_version_id=true sp_cleanup.add_generated_serial_version_id=false -sp_cleanup.add_missing_annotations=false +sp_cleanup.add_missing_annotations=true sp_cleanup.add_missing_deprecated_annotations=true sp_cleanup.add_missing_methods=false sp_cleanup.add_missing_nls_tags=false sp_cleanup.add_missing_override_annotations=true -sp_cleanup.add_missing_override_annotations_interface_methods=false +sp_cleanup.add_missing_override_annotations_interface_methods=true sp_cleanup.add_serial_version_id=false sp_cleanup.always_use_blocks=true sp_cleanup.always_use_parentheses_in_expressions=false sp_cleanup.always_use_this_for_non_static_field_access=false sp_cleanup.always_use_this_for_non_static_method_access=false +sp_cleanup.convert_functional_interfaces=false sp_cleanup.convert_to_enhanced_for_loop=false sp_cleanup.correct_indentation=false sp_cleanup.format_source_code=true sp_cleanup.format_source_code_changes_only=true +sp_cleanup.insert_inferred_type_arguments=false sp_cleanup.make_local_variable_final=false sp_cleanup.make_parameters_final=false sp_cleanup.make_private_fields_final=true @@ -32,19 +34,20 @@ sp_cleanup.make_variable_declarations_final=false sp_cleanup.never_use_blocks=false sp_cleanup.never_use_parentheses_in_expressions=true sp_cleanup.on_save_use_additional_actions=true -sp_cleanup.organize_imports=false +sp_cleanup.organize_imports=true sp_cleanup.qualify_static_field_accesses_with_declaring_class=false sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true sp_cleanup.qualify_static_member_accesses_with_declaring_class=false sp_cleanup.qualify_static_method_accesses_with_declaring_class=false sp_cleanup.remove_private_constructors=true +sp_cleanup.remove_redundant_type_arguments=true sp_cleanup.remove_trailing_whitespaces=true sp_cleanup.remove_trailing_whitespaces_all=true sp_cleanup.remove_trailing_whitespaces_ignore_empty=false -sp_cleanup.remove_unnecessary_casts=false -sp_cleanup.remove_unnecessary_nls_tags=false -sp_cleanup.remove_unused_imports=false +sp_cleanup.remove_unnecessary_casts=true +sp_cleanup.remove_unnecessary_nls_tags=true +sp_cleanup.remove_unused_imports=true sp_cleanup.remove_unused_local_variables=false sp_cleanup.remove_unused_private_fields=true sp_cleanup.remove_unused_private_members=false @@ -52,8 +55,10 @@ sp_cleanup.remove_unused_private_methods=true sp_cleanup.remove_unused_private_types=true sp_cleanup.sort_members=false sp_cleanup.sort_members_all=false -sp_cleanup.use_blocks=false +sp_cleanup.use_anonymous_class_creation=false +sp_cleanup.use_blocks=true sp_cleanup.use_blocks_only_for_return_and_throw=false +sp_cleanup.use_lambda=false sp_cleanup.use_parentheses_in_expressions=false sp_cleanup.use_this_for_non_static_field_access=false sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true diff --git a/org.eclipse.jgit.http.server/.settings/org.eclipse.jdt.ui.prefs b/org.eclipse.jgit.http.server/.settings/org.eclipse.jdt.ui.prefs index c336cce6ed..80d38d9c44 100644 --- a/org.eclipse.jgit.http.server/.settings/org.eclipse.jdt.ui.prefs +++ b/org.eclipse.jgit.http.server/.settings/org.eclipse.jdt.ui.prefs @@ -9,21 +9,23 @@ org.eclipse.jdt.ui.staticondemandthreshold=99 org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8"?><templates/> sp_cleanup.add_default_serial_version_id=true sp_cleanup.add_generated_serial_version_id=false -sp_cleanup.add_missing_annotations=false +sp_cleanup.add_missing_annotations=true sp_cleanup.add_missing_deprecated_annotations=true sp_cleanup.add_missing_methods=false sp_cleanup.add_missing_nls_tags=false sp_cleanup.add_missing_override_annotations=true -sp_cleanup.add_missing_override_annotations_interface_methods=false +sp_cleanup.add_missing_override_annotations_interface_methods=true sp_cleanup.add_serial_version_id=false sp_cleanup.always_use_blocks=true sp_cleanup.always_use_parentheses_in_expressions=false sp_cleanup.always_use_this_for_non_static_field_access=false sp_cleanup.always_use_this_for_non_static_method_access=false +sp_cleanup.convert_functional_interfaces=false sp_cleanup.convert_to_enhanced_for_loop=false sp_cleanup.correct_indentation=false sp_cleanup.format_source_code=true sp_cleanup.format_source_code_changes_only=true +sp_cleanup.insert_inferred_type_arguments=false sp_cleanup.make_local_variable_final=false sp_cleanup.make_parameters_final=false sp_cleanup.make_private_fields_final=true @@ -32,19 +34,20 @@ sp_cleanup.make_variable_declarations_final=false sp_cleanup.never_use_blocks=false sp_cleanup.never_use_parentheses_in_expressions=true sp_cleanup.on_save_use_additional_actions=true -sp_cleanup.organize_imports=false +sp_cleanup.organize_imports=true sp_cleanup.qualify_static_field_accesses_with_declaring_class=false sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true sp_cleanup.qualify_static_member_accesses_with_declaring_class=false sp_cleanup.qualify_static_method_accesses_with_declaring_class=false sp_cleanup.remove_private_constructors=true +sp_cleanup.remove_redundant_type_arguments=true sp_cleanup.remove_trailing_whitespaces=true sp_cleanup.remove_trailing_whitespaces_all=true sp_cleanup.remove_trailing_whitespaces_ignore_empty=false -sp_cleanup.remove_unnecessary_casts=false -sp_cleanup.remove_unnecessary_nls_tags=false -sp_cleanup.remove_unused_imports=false +sp_cleanup.remove_unnecessary_casts=true +sp_cleanup.remove_unnecessary_nls_tags=true +sp_cleanup.remove_unused_imports=true sp_cleanup.remove_unused_local_variables=false sp_cleanup.remove_unused_private_fields=true sp_cleanup.remove_unused_private_members=false @@ -52,8 +55,10 @@ sp_cleanup.remove_unused_private_methods=true sp_cleanup.remove_unused_private_types=true sp_cleanup.sort_members=false sp_cleanup.sort_members_all=false -sp_cleanup.use_blocks=false +sp_cleanup.use_anonymous_class_creation=false +sp_cleanup.use_blocks=true sp_cleanup.use_blocks_only_for_return_and_throw=false +sp_cleanup.use_lambda=false sp_cleanup.use_parentheses_in_expressions=false sp_cleanup.use_this_for_non_static_field_access=false sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitSmartHttpTools.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitSmartHttpTools.java index 03c9d8da69..cfe48223da 100644 --- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitSmartHttpTools.java +++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitSmartHttpTools.java @@ -201,7 +201,7 @@ public class GitSmartHttpTools { } else { if (httpStatus < 400) ServletUtils.consumeRequestBody(req); - res.sendError(httpStatus); + res.sendError(httpStatus, textForGit); } } diff --git a/org.eclipse.jgit.http.test/.settings/org.eclipse.jdt.ui.prefs b/org.eclipse.jgit.http.test/.settings/org.eclipse.jdt.ui.prefs index c336cce6ed..80d38d9c44 100644 --- a/org.eclipse.jgit.http.test/.settings/org.eclipse.jdt.ui.prefs +++ b/org.eclipse.jgit.http.test/.settings/org.eclipse.jdt.ui.prefs @@ -9,21 +9,23 @@ org.eclipse.jdt.ui.staticondemandthreshold=99 org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8"?><templates/> sp_cleanup.add_default_serial_version_id=true sp_cleanup.add_generated_serial_version_id=false -sp_cleanup.add_missing_annotations=false +sp_cleanup.add_missing_annotations=true sp_cleanup.add_missing_deprecated_annotations=true sp_cleanup.add_missing_methods=false sp_cleanup.add_missing_nls_tags=false sp_cleanup.add_missing_override_annotations=true -sp_cleanup.add_missing_override_annotations_interface_methods=false +sp_cleanup.add_missing_override_annotations_interface_methods=true sp_cleanup.add_serial_version_id=false sp_cleanup.always_use_blocks=true sp_cleanup.always_use_parentheses_in_expressions=false sp_cleanup.always_use_this_for_non_static_field_access=false sp_cleanup.always_use_this_for_non_static_method_access=false +sp_cleanup.convert_functional_interfaces=false sp_cleanup.convert_to_enhanced_for_loop=false sp_cleanup.correct_indentation=false sp_cleanup.format_source_code=true sp_cleanup.format_source_code_changes_only=true +sp_cleanup.insert_inferred_type_arguments=false sp_cleanup.make_local_variable_final=false sp_cleanup.make_parameters_final=false sp_cleanup.make_private_fields_final=true @@ -32,19 +34,20 @@ sp_cleanup.make_variable_declarations_final=false sp_cleanup.never_use_blocks=false sp_cleanup.never_use_parentheses_in_expressions=true sp_cleanup.on_save_use_additional_actions=true -sp_cleanup.organize_imports=false +sp_cleanup.organize_imports=true sp_cleanup.qualify_static_field_accesses_with_declaring_class=false sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true sp_cleanup.qualify_static_member_accesses_with_declaring_class=false sp_cleanup.qualify_static_method_accesses_with_declaring_class=false sp_cleanup.remove_private_constructors=true +sp_cleanup.remove_redundant_type_arguments=true sp_cleanup.remove_trailing_whitespaces=true sp_cleanup.remove_trailing_whitespaces_all=true sp_cleanup.remove_trailing_whitespaces_ignore_empty=false -sp_cleanup.remove_unnecessary_casts=false -sp_cleanup.remove_unnecessary_nls_tags=false -sp_cleanup.remove_unused_imports=false +sp_cleanup.remove_unnecessary_casts=true +sp_cleanup.remove_unnecessary_nls_tags=true +sp_cleanup.remove_unused_imports=true sp_cleanup.remove_unused_local_variables=false sp_cleanup.remove_unused_private_fields=true sp_cleanup.remove_unused_private_members=false @@ -52,8 +55,10 @@ sp_cleanup.remove_unused_private_methods=true sp_cleanup.remove_unused_private_types=true sp_cleanup.sort_members=false sp_cleanup.sort_members_all=false -sp_cleanup.use_blocks=false +sp_cleanup.use_anonymous_class_creation=false +sp_cleanup.use_blocks=true sp_cleanup.use_blocks_only_for_return_and_throw=false +sp_cleanup.use_lambda=false sp_cleanup.use_parentheses_in_expressions=false sp_cleanup.use_this_for_non_static_field_access=false sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true diff --git a/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF index 421fa8ad61..08bd0ef14f 100644 --- a/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF @@ -8,6 +8,8 @@ Bundle-Localization: plugin 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.apache.commons.codec;version="1.6.0", + org.apache.commons.codec.binary;version="1.6.0", org.eclipse.jetty.continuation;version="[9.4.5,10.0.0)", org.eclipse.jetty.http;version="[9.4.5,10.0.0)", org.eclipse.jetty.io;version="[9.4.5,10.0.0)", 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 8cadca5235..51b79903c6 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 @@ -83,6 +83,7 @@ import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jgit.errors.RemoteRepositoryException; import org.eclipse.jgit.errors.TransportException; +import org.eclipse.jgit.errors.UnsupportedCredentialItem; import org.eclipse.jgit.http.server.GitServlet; import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription; @@ -105,12 +106,15 @@ import org.eclipse.jgit.lib.StoredConfig; import org.eclipse.jgit.revwalk.RevBlob; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.storage.file.FileBasedConfig; +import org.eclipse.jgit.transport.CredentialItem; +import org.eclipse.jgit.transport.CredentialsProvider; import org.eclipse.jgit.transport.FetchConnection; import org.eclipse.jgit.transport.HttpTransport; import org.eclipse.jgit.transport.RemoteRefUpdate; import org.eclipse.jgit.transport.Transport; import org.eclipse.jgit.transport.TransportHttp; import org.eclipse.jgit.transport.URIish; +import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider; import org.eclipse.jgit.transport.http.HttpConnectionFactory; import org.eclipse.jgit.transport.http.JDKHttpConnectionFactory; import org.eclipse.jgit.transport.http.apache.HttpClientConnectionFactory; @@ -129,12 +133,19 @@ public class SmartClientSmartServerTest extends HttpTestCase { private Repository remoteRepository; + private CredentialsProvider testCredentials = new UsernamePasswordCredentialsProvider( + AppServer.username, AppServer.password); + private URIish remoteURI; private URIish brokenURI; private URIish redirectURI; + private URIish authURI; + + private URIish authOnPostURI; + private RevBlob A_txt; private RevCommit A, B; @@ -169,7 +180,11 @@ public class SmartClientSmartServerTest extends HttpTestCase { ServletContextHandler broken = addBrokenContext(gs, src, srcName); - ServletContextHandler redirect = addRedirectContext(gs, src, srcName); + ServletContextHandler redirect = addRedirectContext(gs); + + ServletContextHandler auth = addAuthContext(gs, "auth"); + + ServletContextHandler authOnPost = addAuthContext(gs, "pauth", "POST"); server.setUp(); @@ -177,6 +192,8 @@ public class SmartClientSmartServerTest extends HttpTestCase { remoteURI = toURIish(app, srcName); brokenURI = toURIish(broken, srcName); redirectURI = toURIish(redirect, srcName); + authURI = toURIish(auth, srcName); + authOnPostURI = toURIish(authOnPost, srcName); A_txt = src.blob("A"); A = src.commit().add("A_txt", A_txt).create(); @@ -271,9 +288,14 @@ public class SmartClientSmartServerTest extends HttpTestCase { return broken; } - @SuppressWarnings("unused") - private ServletContextHandler addRedirectContext(GitServlet gs, - TestRepository<Repository> src, String srcName) { + private ServletContextHandler addAuthContext(GitServlet gs, + String contextPath, String... methods) { + ServletContextHandler auth = server.addContext('/' + contextPath); + auth.addServlet(new ServletHolder(gs), "/*"); + return server.authBasic(auth, methods); + } + + private ServletContextHandler addRedirectContext(GitServlet gs) { ServletContextHandler redirect = server.addContext("/redirect"); redirect.addFilter(new FilterHolder(new Filter() { @@ -283,6 +305,11 @@ public class SmartClientSmartServerTest extends HttpTestCase { private Pattern responsePattern = Pattern .compile("/response/(\\d+)/(30[1237])/"); + // Enables tests to specify the context that the request should be + // redirected to in the end. If not present, redirects got to the + // normal /git context. + private Pattern targetPattern = Pattern.compile("/target(/\\w+)/"); + @Override public void init(FilterConfig filterConfig) throws ServletException { @@ -322,18 +349,25 @@ public class SmartClientSmartServerTest extends HttpTestCase { .parseUnsignedInt(matcher.group(1)); responseCode = Integer.parseUnsignedInt(matcher.group(2)); if (--nofRedirects <= 0) { - urlString = fullUrl.substring(0, matcher.start()) + '/' - + fullUrl.substring(matcher.end()); + urlString = urlString.substring(0, matcher.start()) + + '/' + urlString.substring(matcher.end()); } else { - urlString = fullUrl.substring(0, matcher.start()) + urlString = urlString.substring(0, matcher.start()) + "/response/" + nofRedirects + "/" + responseCode + '/' - + fullUrl.substring(matcher.end()); + + urlString.substring(matcher.end()); } } httpServletResponse.setStatus(responseCode); if (nofRedirects <= 0) { - urlString = urlString.replace("/redirect", "/git"); + String targetContext = "/git"; + matcher = targetPattern.matcher(urlString); + if (matcher.find()) { + urlString = urlString.substring(0, matcher.start()) + + '/' + urlString.substring(matcher.end()); + targetContext = matcher.group(1); + } + urlString = urlString.replace("/redirect", targetContext); } httpServletResponse.setHeader(HttpSupport.HDR_LOCATION, urlString); @@ -561,9 +595,9 @@ public class SmartClientSmartServerTest extends HttpTestCase { t.fetch(NullProgressMonitor.INSTANCE, mirror(master)); fail("Should have failed (too many redirects)"); } catch (TransportException e) { - String expectedMessageBegin = MessageFormat.format( - JGitText.get().redirectLimitExceeded, remoteUri, "3", - remoteUri.replace("/4/", "/1/") + '/', ""); + String expectedMessageBegin = remoteUri.toString() + ": " + + MessageFormat.format(JGitText.get().redirectLimitExceeded, + "3", remoteUri.replace("/4/", "/1/") + '/', ""); String message = e.getMessage(); if (message.length() > expectedMessageBegin.length()) { message = message.substring(0, expectedMessageBegin.length()); @@ -582,7 +616,7 @@ public class SmartClientSmartServerTest extends HttpTestCase { t.fetch(NullProgressMonitor.INSTANCE, mirror(master)); fail("Should have failed (redirect loop)"); } catch (TransportException e) { - assertTrue(e.getMessage().contains("redirected more than")); + assertTrue(e.getMessage().contains("Redirected more than")); } } @@ -669,6 +703,215 @@ public class SmartClientSmartServerTest extends HttpTestCase { } @Test + public void testInitialClone_WithAuthentication() throws Exception { + Repository dst = createBareRepository(); + assertFalse(dst.hasObject(A_txt)); + + try (Transport t = Transport.open(dst, authURI)) { + t.setCredentialsProvider(testCredentials); + 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(3, requests.size()); + + AccessEvent info = requests.get(0); + assertEquals("GET", info.getMethod()); + assertEquals(401, info.getStatus()); + + info = requests.get(1); + assertEquals("GET", info.getMethod()); + assertEquals(join(authURI, "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 service = requests.get(2); + assertEquals("POST", service.getMethod()); + assertEquals(join(authURI, "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 testInitialClone_WithAuthenticationNoCredentials() + throws Exception { + Repository dst = createBareRepository(); + assertFalse(dst.hasObject(A_txt)); + + try (Transport t = Transport.open(dst, authURI)) { + t.fetch(NullProgressMonitor.INSTANCE, mirror(master)); + fail("Should not have succeeded -- no authentication"); + } catch (TransportException e) { + String msg = e.getMessage(); + assertTrue("Unexpected exception message: " + msg, + msg.contains("no CredentialsProvider")); + } + List<AccessEvent> requests = getRequests(); + assertEquals(1, requests.size()); + + AccessEvent info = requests.get(0); + assertEquals("GET", info.getMethod()); + assertEquals(401, info.getStatus()); + } + + @Test + public void testInitialClone_WithAuthenticationWrongCredentials() + throws Exception { + Repository dst = createBareRepository(); + assertFalse(dst.hasObject(A_txt)); + + try (Transport t = Transport.open(dst, authURI)) { + t.setCredentialsProvider(new UsernamePasswordCredentialsProvider( + AppServer.username, "wrongpassword")); + t.fetch(NullProgressMonitor.INSTANCE, mirror(master)); + fail("Should not have succeeded -- wrong password"); + } catch (TransportException e) { + String msg = e.getMessage(); + assertTrue("Unexpected exception message: " + msg, + msg.contains("auth")); + } + List<AccessEvent> requests = getRequests(); + // Once without authentication plus three re-tries with authentication + assertEquals(4, requests.size()); + + for (AccessEvent event : requests) { + assertEquals("GET", event.getMethod()); + assertEquals(401, event.getStatus()); + } + } + + @Test + public void testInitialClone_WithAuthenticationAfterRedirect() + throws Exception { + Repository dst = createBareRepository(); + assertFalse(dst.hasObject(A_txt)); + + URIish cloneFrom = extendPath(redirectURI, "/target/auth"); + CredentialsProvider uriSpecificCredentialsProvider = new UsernamePasswordCredentialsProvider( + "unknown", "none") { + @Override + public boolean get(URIish uri, CredentialItem... items) + throws UnsupportedCredentialItem { + // Only return the true credentials if the uri path starts with + // /auth. This ensures that we do provide the correct + // credentials only for the URi after the redirect, making the + // test fail if we should be asked for the credentials for the + // original URI. + if (uri.getPath().startsWith("/auth")) { + return testCredentials.get(uri, items); + } + return super.get(uri, items); + } + }; + try (Transport t = Transport.open(dst, cloneFrom)) { + t.setCredentialsProvider(uriSpecificCredentialsProvider); + 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 redirect = requests.get(0); + assertEquals("GET", redirect.getMethod()); + assertEquals(join(cloneFrom, "info/refs"), redirect.getPath()); + assertEquals(301, redirect.getStatus()); + + AccessEvent info = requests.get(1); + assertEquals("GET", info.getMethod()); + assertEquals(join(authURI, "info/refs"), info.getPath()); + assertEquals(401, info.getStatus()); + + info = requests.get(2); + assertEquals("GET", info.getMethod()); + assertEquals(join(authURI, "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 service = requests.get(3); + assertEquals("POST", service.getMethod()); + assertEquals(join(authURI, "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 testInitialClone_WithAuthenticationOnPostOnly() + throws Exception { + Repository dst = createBareRepository(); + assertFalse(dst.hasObject(A_txt)); + + try (Transport t = Transport.open(dst, authOnPostURI)) { + t.setCredentialsProvider(testCredentials); + 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(3, requests.size()); + + AccessEvent info = requests.get(0); + assertEquals("GET", info.getMethod()); + assertEquals(join(authOnPostURI, "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 service = requests.get(1); + assertEquals("POST", service.getMethod()); + assertEquals(join(authOnPostURI, "git-upload-pack"), service.getPath()); + assertEquals(401, service.getStatus()); + + service = requests.get(2); + assertEquals("POST", service.getMethod()); + assertEquals(join(authOnPostURI, "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. // diff --git a/org.eclipse.jgit.junit.http/.settings/org.eclipse.jdt.ui.prefs b/org.eclipse.jgit.junit.http/.settings/org.eclipse.jdt.ui.prefs index c336cce6ed..80d38d9c44 100644 --- a/org.eclipse.jgit.junit.http/.settings/org.eclipse.jdt.ui.prefs +++ b/org.eclipse.jgit.junit.http/.settings/org.eclipse.jdt.ui.prefs @@ -9,21 +9,23 @@ org.eclipse.jdt.ui.staticondemandthreshold=99 org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8"?><templates/> sp_cleanup.add_default_serial_version_id=true sp_cleanup.add_generated_serial_version_id=false -sp_cleanup.add_missing_annotations=false +sp_cleanup.add_missing_annotations=true sp_cleanup.add_missing_deprecated_annotations=true sp_cleanup.add_missing_methods=false sp_cleanup.add_missing_nls_tags=false sp_cleanup.add_missing_override_annotations=true -sp_cleanup.add_missing_override_annotations_interface_methods=false +sp_cleanup.add_missing_override_annotations_interface_methods=true sp_cleanup.add_serial_version_id=false sp_cleanup.always_use_blocks=true sp_cleanup.always_use_parentheses_in_expressions=false sp_cleanup.always_use_this_for_non_static_field_access=false sp_cleanup.always_use_this_for_non_static_method_access=false +sp_cleanup.convert_functional_interfaces=false sp_cleanup.convert_to_enhanced_for_loop=false sp_cleanup.correct_indentation=false sp_cleanup.format_source_code=true sp_cleanup.format_source_code_changes_only=true +sp_cleanup.insert_inferred_type_arguments=false sp_cleanup.make_local_variable_final=false sp_cleanup.make_parameters_final=false sp_cleanup.make_private_fields_final=true @@ -32,19 +34,20 @@ sp_cleanup.make_variable_declarations_final=false sp_cleanup.never_use_blocks=false sp_cleanup.never_use_parentheses_in_expressions=true sp_cleanup.on_save_use_additional_actions=true -sp_cleanup.organize_imports=false +sp_cleanup.organize_imports=true sp_cleanup.qualify_static_field_accesses_with_declaring_class=false sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true sp_cleanup.qualify_static_member_accesses_with_declaring_class=false sp_cleanup.qualify_static_method_accesses_with_declaring_class=false sp_cleanup.remove_private_constructors=true +sp_cleanup.remove_redundant_type_arguments=true sp_cleanup.remove_trailing_whitespaces=true sp_cleanup.remove_trailing_whitespaces_all=true sp_cleanup.remove_trailing_whitespaces_ignore_empty=false -sp_cleanup.remove_unnecessary_casts=false -sp_cleanup.remove_unnecessary_nls_tags=false -sp_cleanup.remove_unused_imports=false +sp_cleanup.remove_unnecessary_casts=true +sp_cleanup.remove_unnecessary_nls_tags=true +sp_cleanup.remove_unused_imports=true sp_cleanup.remove_unused_local_variables=false sp_cleanup.remove_unused_private_fields=true sp_cleanup.remove_unused_private_members=false @@ -52,8 +55,10 @@ sp_cleanup.remove_unused_private_methods=true sp_cleanup.remove_unused_private_types=true sp_cleanup.sort_members=false sp_cleanup.sort_members_all=false -sp_cleanup.use_blocks=false +sp_cleanup.use_anonymous_class_creation=false +sp_cleanup.use_blocks=true sp_cleanup.use_blocks_only_for_return_and_throw=false +sp_cleanup.use_lambda=false sp_cleanup.use_parentheses_in_expressions=false sp_cleanup.use_this_for_non_static_field_access=false sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true 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 69e2cd5957..e257cf65b6 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 @@ -55,6 +55,7 @@ import java.net.UnknownHostException; import java.nio.file.Files; import java.util.ArrayList; import java.util.List; +import java.util.Locale; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -97,6 +98,9 @@ public class AppServer { /** SSL keystore password; must have at least 6 characters. */ private static final String keyPassword = "mykeys"; + /** Role for authentication. */ + private static final String authRole = "can-access"; + static { // Install a logger that throws warning messages. // @@ -136,10 +140,10 @@ public class AppServer { /** * @param port - * for https, may be zero to allocate a port dynamically + * for http, may be zero to allocate a port dynamically * @param sslPort * for https,may be zero to allocate a port dynamically. If - * negative, the server will be set up without https support.. + * negative, the server will be set up without https support. * @since 4.9 */ public AppServer(int port, int sslPort) { @@ -264,9 +268,10 @@ public class AppServer { return ctx; } - public ServletContextHandler authBasic(ServletContextHandler ctx) { + public ServletContextHandler authBasic(ServletContextHandler ctx, + String... methods) { assertNotYetSetUp(); - auth(ctx, new BasicAuthenticator()); + auth(ctx, new BasicAuthenticator(), methods); return ctx; } @@ -301,22 +306,36 @@ public class AppServer { } } - private void auth(ServletContextHandler ctx, Authenticator authType) { - final String role = "can-access"; - - AbstractLoginService users = new TestMappedLoginService(role); + private ConstraintMapping createConstraintMapping() { ConstraintMapping cm = new ConstraintMapping(); cm.setConstraint(new Constraint()); cm.getConstraint().setAuthenticate(true); cm.getConstraint().setDataConstraint(Constraint.DC_NONE); - cm.getConstraint().setRoles(new String[] { role }); + cm.getConstraint().setRoles(new String[] { authRole }); cm.setPathSpec("/*"); + return cm; + } + + private void auth(ServletContextHandler ctx, Authenticator authType, + String... methods) { + AbstractLoginService users = new TestMappedLoginService(authRole); + List<ConstraintMapping> mappings = new ArrayList<>(); + if (methods == null || methods.length == 0) { + mappings.add(createConstraintMapping()); + } else { + for (String method : methods) { + ConstraintMapping cm = createConstraintMapping(); + cm.setMethod(method.toUpperCase(Locale.ROOT)); + mappings.add(cm); + } + } ConstraintSecurityHandler sec = new ConstraintSecurityHandler(); sec.setRealmName(realm); sec.setAuthenticator(authType); sec.setLoginService(users); - sec.setConstraintMappings(new ConstraintMapping[] { cm }); + sec.setConstraintMappings( + mappings.toArray(new ConstraintMapping[mappings.size()])); sec.setHandler(ctx); contexts.removeHandler(ctx); diff --git a/org.eclipse.jgit.junit/.settings/org.eclipse.jdt.ui.prefs b/org.eclipse.jgit.junit/.settings/org.eclipse.jdt.ui.prefs index c336cce6ed..80d38d9c44 100644 --- a/org.eclipse.jgit.junit/.settings/org.eclipse.jdt.ui.prefs +++ b/org.eclipse.jgit.junit/.settings/org.eclipse.jdt.ui.prefs @@ -9,21 +9,23 @@ org.eclipse.jdt.ui.staticondemandthreshold=99 org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8"?><templates/> sp_cleanup.add_default_serial_version_id=true sp_cleanup.add_generated_serial_version_id=false -sp_cleanup.add_missing_annotations=false +sp_cleanup.add_missing_annotations=true sp_cleanup.add_missing_deprecated_annotations=true sp_cleanup.add_missing_methods=false sp_cleanup.add_missing_nls_tags=false sp_cleanup.add_missing_override_annotations=true -sp_cleanup.add_missing_override_annotations_interface_methods=false +sp_cleanup.add_missing_override_annotations_interface_methods=true sp_cleanup.add_serial_version_id=false sp_cleanup.always_use_blocks=true sp_cleanup.always_use_parentheses_in_expressions=false sp_cleanup.always_use_this_for_non_static_field_access=false sp_cleanup.always_use_this_for_non_static_method_access=false +sp_cleanup.convert_functional_interfaces=false sp_cleanup.convert_to_enhanced_for_loop=false sp_cleanup.correct_indentation=false sp_cleanup.format_source_code=true sp_cleanup.format_source_code_changes_only=true +sp_cleanup.insert_inferred_type_arguments=false sp_cleanup.make_local_variable_final=false sp_cleanup.make_parameters_final=false sp_cleanup.make_private_fields_final=true @@ -32,19 +34,20 @@ sp_cleanup.make_variable_declarations_final=false sp_cleanup.never_use_blocks=false sp_cleanup.never_use_parentheses_in_expressions=true sp_cleanup.on_save_use_additional_actions=true -sp_cleanup.organize_imports=false +sp_cleanup.organize_imports=true sp_cleanup.qualify_static_field_accesses_with_declaring_class=false sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true sp_cleanup.qualify_static_member_accesses_with_declaring_class=false sp_cleanup.qualify_static_method_accesses_with_declaring_class=false sp_cleanup.remove_private_constructors=true +sp_cleanup.remove_redundant_type_arguments=true sp_cleanup.remove_trailing_whitespaces=true sp_cleanup.remove_trailing_whitespaces_all=true sp_cleanup.remove_trailing_whitespaces_ignore_empty=false -sp_cleanup.remove_unnecessary_casts=false -sp_cleanup.remove_unnecessary_nls_tags=false -sp_cleanup.remove_unused_imports=false +sp_cleanup.remove_unnecessary_casts=true +sp_cleanup.remove_unnecessary_nls_tags=true +sp_cleanup.remove_unused_imports=true sp_cleanup.remove_unused_local_variables=false sp_cleanup.remove_unused_private_fields=true sp_cleanup.remove_unused_private_members=false @@ -52,8 +55,10 @@ sp_cleanup.remove_unused_private_methods=true sp_cleanup.remove_unused_private_types=true sp_cleanup.sort_members=false sp_cleanup.sort_members_all=false -sp_cleanup.use_blocks=false +sp_cleanup.use_anonymous_class_creation=false +sp_cleanup.use_blocks=true sp_cleanup.use_blocks_only_for_return_and_throw=false +sp_cleanup.use_lambda=false sp_cleanup.use_parentheses_in_expressions=false sp_cleanup.use_this_for_non_static_field_access=false sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true diff --git a/org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.jdt.ui.prefs b/org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.jdt.ui.prefs index c336cce6ed..80d38d9c44 100644 --- a/org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.jdt.ui.prefs +++ b/org.eclipse.jgit.lfs.server.test/.settings/org.eclipse.jdt.ui.prefs @@ -9,21 +9,23 @@ org.eclipse.jdt.ui.staticondemandthreshold=99 org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8"?><templates/> sp_cleanup.add_default_serial_version_id=true sp_cleanup.add_generated_serial_version_id=false -sp_cleanup.add_missing_annotations=false +sp_cleanup.add_missing_annotations=true sp_cleanup.add_missing_deprecated_annotations=true sp_cleanup.add_missing_methods=false sp_cleanup.add_missing_nls_tags=false sp_cleanup.add_missing_override_annotations=true -sp_cleanup.add_missing_override_annotations_interface_methods=false +sp_cleanup.add_missing_override_annotations_interface_methods=true sp_cleanup.add_serial_version_id=false sp_cleanup.always_use_blocks=true sp_cleanup.always_use_parentheses_in_expressions=false sp_cleanup.always_use_this_for_non_static_field_access=false sp_cleanup.always_use_this_for_non_static_method_access=false +sp_cleanup.convert_functional_interfaces=false sp_cleanup.convert_to_enhanced_for_loop=false sp_cleanup.correct_indentation=false sp_cleanup.format_source_code=true sp_cleanup.format_source_code_changes_only=true +sp_cleanup.insert_inferred_type_arguments=false sp_cleanup.make_local_variable_final=false sp_cleanup.make_parameters_final=false sp_cleanup.make_private_fields_final=true @@ -32,19 +34,20 @@ sp_cleanup.make_variable_declarations_final=false sp_cleanup.never_use_blocks=false sp_cleanup.never_use_parentheses_in_expressions=true sp_cleanup.on_save_use_additional_actions=true -sp_cleanup.organize_imports=false +sp_cleanup.organize_imports=true sp_cleanup.qualify_static_field_accesses_with_declaring_class=false sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true sp_cleanup.qualify_static_member_accesses_with_declaring_class=false sp_cleanup.qualify_static_method_accesses_with_declaring_class=false sp_cleanup.remove_private_constructors=true +sp_cleanup.remove_redundant_type_arguments=true sp_cleanup.remove_trailing_whitespaces=true sp_cleanup.remove_trailing_whitespaces_all=true sp_cleanup.remove_trailing_whitespaces_ignore_empty=false -sp_cleanup.remove_unnecessary_casts=false -sp_cleanup.remove_unnecessary_nls_tags=false -sp_cleanup.remove_unused_imports=false +sp_cleanup.remove_unnecessary_casts=true +sp_cleanup.remove_unnecessary_nls_tags=true +sp_cleanup.remove_unused_imports=true sp_cleanup.remove_unused_local_variables=false sp_cleanup.remove_unused_private_fields=true sp_cleanup.remove_unused_private_members=false @@ -52,8 +55,10 @@ sp_cleanup.remove_unused_private_methods=true sp_cleanup.remove_unused_private_types=true sp_cleanup.sort_members=false sp_cleanup.sort_members_all=false -sp_cleanup.use_blocks=false +sp_cleanup.use_anonymous_class_creation=false +sp_cleanup.use_blocks=true sp_cleanup.use_blocks_only_for_return_and_throw=false +sp_cleanup.use_lambda=false sp_cleanup.use_parentheses_in_expressions=false sp_cleanup.use_this_for_non_static_field_access=false sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true diff --git a/org.eclipse.jgit.lfs.server/.settings/org.eclipse.jdt.ui.prefs b/org.eclipse.jgit.lfs.server/.settings/org.eclipse.jdt.ui.prefs index c336cce6ed..80d38d9c44 100644 --- a/org.eclipse.jgit.lfs.server/.settings/org.eclipse.jdt.ui.prefs +++ b/org.eclipse.jgit.lfs.server/.settings/org.eclipse.jdt.ui.prefs @@ -9,21 +9,23 @@ org.eclipse.jdt.ui.staticondemandthreshold=99 org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8"?><templates/> sp_cleanup.add_default_serial_version_id=true sp_cleanup.add_generated_serial_version_id=false -sp_cleanup.add_missing_annotations=false +sp_cleanup.add_missing_annotations=true sp_cleanup.add_missing_deprecated_annotations=true sp_cleanup.add_missing_methods=false sp_cleanup.add_missing_nls_tags=false sp_cleanup.add_missing_override_annotations=true -sp_cleanup.add_missing_override_annotations_interface_methods=false +sp_cleanup.add_missing_override_annotations_interface_methods=true sp_cleanup.add_serial_version_id=false sp_cleanup.always_use_blocks=true sp_cleanup.always_use_parentheses_in_expressions=false sp_cleanup.always_use_this_for_non_static_field_access=false sp_cleanup.always_use_this_for_non_static_method_access=false +sp_cleanup.convert_functional_interfaces=false sp_cleanup.convert_to_enhanced_for_loop=false sp_cleanup.correct_indentation=false sp_cleanup.format_source_code=true sp_cleanup.format_source_code_changes_only=true +sp_cleanup.insert_inferred_type_arguments=false sp_cleanup.make_local_variable_final=false sp_cleanup.make_parameters_final=false sp_cleanup.make_private_fields_final=true @@ -32,19 +34,20 @@ sp_cleanup.make_variable_declarations_final=false sp_cleanup.never_use_blocks=false sp_cleanup.never_use_parentheses_in_expressions=true sp_cleanup.on_save_use_additional_actions=true -sp_cleanup.organize_imports=false +sp_cleanup.organize_imports=true sp_cleanup.qualify_static_field_accesses_with_declaring_class=false sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true sp_cleanup.qualify_static_member_accesses_with_declaring_class=false sp_cleanup.qualify_static_method_accesses_with_declaring_class=false sp_cleanup.remove_private_constructors=true +sp_cleanup.remove_redundant_type_arguments=true sp_cleanup.remove_trailing_whitespaces=true sp_cleanup.remove_trailing_whitespaces_all=true sp_cleanup.remove_trailing_whitespaces_ignore_empty=false -sp_cleanup.remove_unnecessary_casts=false -sp_cleanup.remove_unnecessary_nls_tags=false -sp_cleanup.remove_unused_imports=false +sp_cleanup.remove_unnecessary_casts=true +sp_cleanup.remove_unnecessary_nls_tags=true +sp_cleanup.remove_unused_imports=true sp_cleanup.remove_unused_local_variables=false sp_cleanup.remove_unused_private_fields=true sp_cleanup.remove_unused_private_members=false @@ -52,8 +55,10 @@ sp_cleanup.remove_unused_private_methods=true sp_cleanup.remove_unused_private_types=true sp_cleanup.sort_members=false sp_cleanup.sort_members_all=false -sp_cleanup.use_blocks=false +sp_cleanup.use_anonymous_class_creation=false +sp_cleanup.use_blocks=true sp_cleanup.use_blocks_only_for_return_and_throw=false +sp_cleanup.use_lambda=false sp_cleanup.use_parentheses_in_expressions=false sp_cleanup.use_this_for_non_static_field_access=false sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true diff --git a/org.eclipse.jgit.lfs.test/.settings/org.eclipse.jdt.ui.prefs b/org.eclipse.jgit.lfs.test/.settings/org.eclipse.jdt.ui.prefs index c336cce6ed..80d38d9c44 100644 --- a/org.eclipse.jgit.lfs.test/.settings/org.eclipse.jdt.ui.prefs +++ b/org.eclipse.jgit.lfs.test/.settings/org.eclipse.jdt.ui.prefs @@ -9,21 +9,23 @@ org.eclipse.jdt.ui.staticondemandthreshold=99 org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8"?><templates/> sp_cleanup.add_default_serial_version_id=true sp_cleanup.add_generated_serial_version_id=false -sp_cleanup.add_missing_annotations=false +sp_cleanup.add_missing_annotations=true sp_cleanup.add_missing_deprecated_annotations=true sp_cleanup.add_missing_methods=false sp_cleanup.add_missing_nls_tags=false sp_cleanup.add_missing_override_annotations=true -sp_cleanup.add_missing_override_annotations_interface_methods=false +sp_cleanup.add_missing_override_annotations_interface_methods=true sp_cleanup.add_serial_version_id=false sp_cleanup.always_use_blocks=true sp_cleanup.always_use_parentheses_in_expressions=false sp_cleanup.always_use_this_for_non_static_field_access=false sp_cleanup.always_use_this_for_non_static_method_access=false +sp_cleanup.convert_functional_interfaces=false sp_cleanup.convert_to_enhanced_for_loop=false sp_cleanup.correct_indentation=false sp_cleanup.format_source_code=true sp_cleanup.format_source_code_changes_only=true +sp_cleanup.insert_inferred_type_arguments=false sp_cleanup.make_local_variable_final=false sp_cleanup.make_parameters_final=false sp_cleanup.make_private_fields_final=true @@ -32,19 +34,20 @@ sp_cleanup.make_variable_declarations_final=false sp_cleanup.never_use_blocks=false sp_cleanup.never_use_parentheses_in_expressions=true sp_cleanup.on_save_use_additional_actions=true -sp_cleanup.organize_imports=false +sp_cleanup.organize_imports=true sp_cleanup.qualify_static_field_accesses_with_declaring_class=false sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true sp_cleanup.qualify_static_member_accesses_with_declaring_class=false sp_cleanup.qualify_static_method_accesses_with_declaring_class=false sp_cleanup.remove_private_constructors=true +sp_cleanup.remove_redundant_type_arguments=true sp_cleanup.remove_trailing_whitespaces=true sp_cleanup.remove_trailing_whitespaces_all=true sp_cleanup.remove_trailing_whitespaces_ignore_empty=false -sp_cleanup.remove_unnecessary_casts=false -sp_cleanup.remove_unnecessary_nls_tags=false -sp_cleanup.remove_unused_imports=false +sp_cleanup.remove_unnecessary_casts=true +sp_cleanup.remove_unnecessary_nls_tags=true +sp_cleanup.remove_unused_imports=true sp_cleanup.remove_unused_local_variables=false sp_cleanup.remove_unused_private_fields=true sp_cleanup.remove_unused_private_members=false @@ -52,8 +55,10 @@ sp_cleanup.remove_unused_private_methods=true sp_cleanup.remove_unused_private_types=true sp_cleanup.sort_members=false sp_cleanup.sort_members_all=false -sp_cleanup.use_blocks=false +sp_cleanup.use_anonymous_class_creation=false +sp_cleanup.use_blocks=true sp_cleanup.use_blocks_only_for_return_and_throw=false +sp_cleanup.use_lambda=false sp_cleanup.use_parentheses_in_expressions=false sp_cleanup.use_this_for_non_static_field_access=false sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true diff --git a/org.eclipse.jgit.lfs/.settings/org.eclipse.jdt.ui.prefs b/org.eclipse.jgit.lfs/.settings/org.eclipse.jdt.ui.prefs index c336cce6ed..80d38d9c44 100644 --- a/org.eclipse.jgit.lfs/.settings/org.eclipse.jdt.ui.prefs +++ b/org.eclipse.jgit.lfs/.settings/org.eclipse.jdt.ui.prefs @@ -9,21 +9,23 @@ org.eclipse.jdt.ui.staticondemandthreshold=99 org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8"?><templates/> sp_cleanup.add_default_serial_version_id=true sp_cleanup.add_generated_serial_version_id=false -sp_cleanup.add_missing_annotations=false +sp_cleanup.add_missing_annotations=true sp_cleanup.add_missing_deprecated_annotations=true sp_cleanup.add_missing_methods=false sp_cleanup.add_missing_nls_tags=false sp_cleanup.add_missing_override_annotations=true -sp_cleanup.add_missing_override_annotations_interface_methods=false +sp_cleanup.add_missing_override_annotations_interface_methods=true sp_cleanup.add_serial_version_id=false sp_cleanup.always_use_blocks=true sp_cleanup.always_use_parentheses_in_expressions=false sp_cleanup.always_use_this_for_non_static_field_access=false sp_cleanup.always_use_this_for_non_static_method_access=false +sp_cleanup.convert_functional_interfaces=false sp_cleanup.convert_to_enhanced_for_loop=false sp_cleanup.correct_indentation=false sp_cleanup.format_source_code=true sp_cleanup.format_source_code_changes_only=true +sp_cleanup.insert_inferred_type_arguments=false sp_cleanup.make_local_variable_final=false sp_cleanup.make_parameters_final=false sp_cleanup.make_private_fields_final=true @@ -32,19 +34,20 @@ sp_cleanup.make_variable_declarations_final=false sp_cleanup.never_use_blocks=false sp_cleanup.never_use_parentheses_in_expressions=true sp_cleanup.on_save_use_additional_actions=true -sp_cleanup.organize_imports=false +sp_cleanup.organize_imports=true sp_cleanup.qualify_static_field_accesses_with_declaring_class=false sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true sp_cleanup.qualify_static_member_accesses_with_declaring_class=false sp_cleanup.qualify_static_method_accesses_with_declaring_class=false sp_cleanup.remove_private_constructors=true +sp_cleanup.remove_redundant_type_arguments=true sp_cleanup.remove_trailing_whitespaces=true sp_cleanup.remove_trailing_whitespaces_all=true sp_cleanup.remove_trailing_whitespaces_ignore_empty=false -sp_cleanup.remove_unnecessary_casts=false -sp_cleanup.remove_unnecessary_nls_tags=false -sp_cleanup.remove_unused_imports=false +sp_cleanup.remove_unnecessary_casts=true +sp_cleanup.remove_unnecessary_nls_tags=true +sp_cleanup.remove_unused_imports=true sp_cleanup.remove_unused_local_variables=false sp_cleanup.remove_unused_private_fields=true sp_cleanup.remove_unused_private_members=false @@ -52,8 +55,10 @@ sp_cleanup.remove_unused_private_methods=true sp_cleanup.remove_unused_private_types=true sp_cleanup.sort_members=false sp_cleanup.sort_members_all=false -sp_cleanup.use_blocks=false +sp_cleanup.use_anonymous_class_creation=false +sp_cleanup.use_blocks=true sp_cleanup.use_blocks_only_for_return_and_throw=false +sp_cleanup.use_lambda=false sp_cleanup.use_parentheses_in_expressions=false sp_cleanup.use_this_for_non_static_field_access=false sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true diff --git a/org.eclipse.jgit.pgm.test/.settings/org.eclipse.jdt.ui.prefs b/org.eclipse.jgit.pgm.test/.settings/org.eclipse.jdt.ui.prefs index c336cce6ed..80d38d9c44 100644 --- a/org.eclipse.jgit.pgm.test/.settings/org.eclipse.jdt.ui.prefs +++ b/org.eclipse.jgit.pgm.test/.settings/org.eclipse.jdt.ui.prefs @@ -9,21 +9,23 @@ org.eclipse.jdt.ui.staticondemandthreshold=99 org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8"?><templates/> sp_cleanup.add_default_serial_version_id=true sp_cleanup.add_generated_serial_version_id=false -sp_cleanup.add_missing_annotations=false +sp_cleanup.add_missing_annotations=true sp_cleanup.add_missing_deprecated_annotations=true sp_cleanup.add_missing_methods=false sp_cleanup.add_missing_nls_tags=false sp_cleanup.add_missing_override_annotations=true -sp_cleanup.add_missing_override_annotations_interface_methods=false +sp_cleanup.add_missing_override_annotations_interface_methods=true sp_cleanup.add_serial_version_id=false sp_cleanup.always_use_blocks=true sp_cleanup.always_use_parentheses_in_expressions=false sp_cleanup.always_use_this_for_non_static_field_access=false sp_cleanup.always_use_this_for_non_static_method_access=false +sp_cleanup.convert_functional_interfaces=false sp_cleanup.convert_to_enhanced_for_loop=false sp_cleanup.correct_indentation=false sp_cleanup.format_source_code=true sp_cleanup.format_source_code_changes_only=true +sp_cleanup.insert_inferred_type_arguments=false sp_cleanup.make_local_variable_final=false sp_cleanup.make_parameters_final=false sp_cleanup.make_private_fields_final=true @@ -32,19 +34,20 @@ sp_cleanup.make_variable_declarations_final=false sp_cleanup.never_use_blocks=false sp_cleanup.never_use_parentheses_in_expressions=true sp_cleanup.on_save_use_additional_actions=true -sp_cleanup.organize_imports=false +sp_cleanup.organize_imports=true sp_cleanup.qualify_static_field_accesses_with_declaring_class=false sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true sp_cleanup.qualify_static_member_accesses_with_declaring_class=false sp_cleanup.qualify_static_method_accesses_with_declaring_class=false sp_cleanup.remove_private_constructors=true +sp_cleanup.remove_redundant_type_arguments=true sp_cleanup.remove_trailing_whitespaces=true sp_cleanup.remove_trailing_whitespaces_all=true sp_cleanup.remove_trailing_whitespaces_ignore_empty=false -sp_cleanup.remove_unnecessary_casts=false -sp_cleanup.remove_unnecessary_nls_tags=false -sp_cleanup.remove_unused_imports=false +sp_cleanup.remove_unnecessary_casts=true +sp_cleanup.remove_unnecessary_nls_tags=true +sp_cleanup.remove_unused_imports=true sp_cleanup.remove_unused_local_variables=false sp_cleanup.remove_unused_private_fields=true sp_cleanup.remove_unused_private_members=false @@ -52,8 +55,10 @@ sp_cleanup.remove_unused_private_methods=true sp_cleanup.remove_unused_private_types=true sp_cleanup.sort_members=false sp_cleanup.sort_members_all=false -sp_cleanup.use_blocks=false +sp_cleanup.use_anonymous_class_creation=false +sp_cleanup.use_blocks=true sp_cleanup.use_blocks_only_for_return_and_throw=false +sp_cleanup.use_lambda=false sp_cleanup.use_parentheses_in_expressions=false sp_cleanup.use_this_for_non_static_field_access=false sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true diff --git a/org.eclipse.jgit.pgm/.settings/org.eclipse.jdt.ui.prefs b/org.eclipse.jgit.pgm/.settings/org.eclipse.jdt.ui.prefs index c336cce6ed..80d38d9c44 100644 --- a/org.eclipse.jgit.pgm/.settings/org.eclipse.jdt.ui.prefs +++ b/org.eclipse.jgit.pgm/.settings/org.eclipse.jdt.ui.prefs @@ -9,21 +9,23 @@ org.eclipse.jdt.ui.staticondemandthreshold=99 org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8"?><templates/> sp_cleanup.add_default_serial_version_id=true sp_cleanup.add_generated_serial_version_id=false -sp_cleanup.add_missing_annotations=false +sp_cleanup.add_missing_annotations=true sp_cleanup.add_missing_deprecated_annotations=true sp_cleanup.add_missing_methods=false sp_cleanup.add_missing_nls_tags=false sp_cleanup.add_missing_override_annotations=true -sp_cleanup.add_missing_override_annotations_interface_methods=false +sp_cleanup.add_missing_override_annotations_interface_methods=true sp_cleanup.add_serial_version_id=false sp_cleanup.always_use_blocks=true sp_cleanup.always_use_parentheses_in_expressions=false sp_cleanup.always_use_this_for_non_static_field_access=false sp_cleanup.always_use_this_for_non_static_method_access=false +sp_cleanup.convert_functional_interfaces=false sp_cleanup.convert_to_enhanced_for_loop=false sp_cleanup.correct_indentation=false sp_cleanup.format_source_code=true sp_cleanup.format_source_code_changes_only=true +sp_cleanup.insert_inferred_type_arguments=false sp_cleanup.make_local_variable_final=false sp_cleanup.make_parameters_final=false sp_cleanup.make_private_fields_final=true @@ -32,19 +34,20 @@ sp_cleanup.make_variable_declarations_final=false sp_cleanup.never_use_blocks=false sp_cleanup.never_use_parentheses_in_expressions=true sp_cleanup.on_save_use_additional_actions=true -sp_cleanup.organize_imports=false +sp_cleanup.organize_imports=true sp_cleanup.qualify_static_field_accesses_with_declaring_class=false sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true sp_cleanup.qualify_static_member_accesses_with_declaring_class=false sp_cleanup.qualify_static_method_accesses_with_declaring_class=false sp_cleanup.remove_private_constructors=true +sp_cleanup.remove_redundant_type_arguments=true sp_cleanup.remove_trailing_whitespaces=true sp_cleanup.remove_trailing_whitespaces_all=true sp_cleanup.remove_trailing_whitespaces_ignore_empty=false -sp_cleanup.remove_unnecessary_casts=false -sp_cleanup.remove_unnecessary_nls_tags=false -sp_cleanup.remove_unused_imports=false +sp_cleanup.remove_unnecessary_casts=true +sp_cleanup.remove_unnecessary_nls_tags=true +sp_cleanup.remove_unused_imports=true sp_cleanup.remove_unused_local_variables=false sp_cleanup.remove_unused_private_fields=true sp_cleanup.remove_unused_private_members=false @@ -52,8 +55,10 @@ sp_cleanup.remove_unused_private_methods=true sp_cleanup.remove_unused_private_types=true sp_cleanup.sort_members=false sp_cleanup.sort_members_all=false -sp_cleanup.use_blocks=false +sp_cleanup.use_anonymous_class_creation=false +sp_cleanup.use_blocks=true sp_cleanup.use_blocks_only_for_return_and_throw=false +sp_cleanup.use_lambda=false sp_cleanup.use_parentheses_in_expressions=false sp_cleanup.use_this_for_non_static_field_access=false sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Clone.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Clone.java index ca5205a4e1..a8eb4742db 100644 --- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Clone.java +++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Clone.java @@ -101,6 +101,9 @@ class Clone extends AbstractFetchCommand implements CloneCommand.Callback { if (localName == null) { try { localName = uri.getHumanishName(); + if (isBare) { + localName = localName + Constants.DOT_GIT_EXT; + } localNameF = new File(SystemReader.getInstance().getProperty( Constants.OS_USER_DIR), localName); } catch (IllegalArgumentException e) { diff --git a/org.eclipse.jgit.test/.settings/org.eclipse.jdt.ui.prefs b/org.eclipse.jgit.test/.settings/org.eclipse.jdt.ui.prefs index c336cce6ed..80d38d9c44 100644 --- a/org.eclipse.jgit.test/.settings/org.eclipse.jdt.ui.prefs +++ b/org.eclipse.jgit.test/.settings/org.eclipse.jdt.ui.prefs @@ -9,21 +9,23 @@ org.eclipse.jdt.ui.staticondemandthreshold=99 org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8"?><templates/> sp_cleanup.add_default_serial_version_id=true sp_cleanup.add_generated_serial_version_id=false -sp_cleanup.add_missing_annotations=false +sp_cleanup.add_missing_annotations=true sp_cleanup.add_missing_deprecated_annotations=true sp_cleanup.add_missing_methods=false sp_cleanup.add_missing_nls_tags=false sp_cleanup.add_missing_override_annotations=true -sp_cleanup.add_missing_override_annotations_interface_methods=false +sp_cleanup.add_missing_override_annotations_interface_methods=true sp_cleanup.add_serial_version_id=false sp_cleanup.always_use_blocks=true sp_cleanup.always_use_parentheses_in_expressions=false sp_cleanup.always_use_this_for_non_static_field_access=false sp_cleanup.always_use_this_for_non_static_method_access=false +sp_cleanup.convert_functional_interfaces=false sp_cleanup.convert_to_enhanced_for_loop=false sp_cleanup.correct_indentation=false sp_cleanup.format_source_code=true sp_cleanup.format_source_code_changes_only=true +sp_cleanup.insert_inferred_type_arguments=false sp_cleanup.make_local_variable_final=false sp_cleanup.make_parameters_final=false sp_cleanup.make_private_fields_final=true @@ -32,19 +34,20 @@ sp_cleanup.make_variable_declarations_final=false sp_cleanup.never_use_blocks=false sp_cleanup.never_use_parentheses_in_expressions=true sp_cleanup.on_save_use_additional_actions=true -sp_cleanup.organize_imports=false +sp_cleanup.organize_imports=true sp_cleanup.qualify_static_field_accesses_with_declaring_class=false sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true sp_cleanup.qualify_static_member_accesses_with_declaring_class=false sp_cleanup.qualify_static_method_accesses_with_declaring_class=false sp_cleanup.remove_private_constructors=true +sp_cleanup.remove_redundant_type_arguments=true sp_cleanup.remove_trailing_whitespaces=true sp_cleanup.remove_trailing_whitespaces_all=true sp_cleanup.remove_trailing_whitespaces_ignore_empty=false -sp_cleanup.remove_unnecessary_casts=false -sp_cleanup.remove_unnecessary_nls_tags=false -sp_cleanup.remove_unused_imports=false +sp_cleanup.remove_unnecessary_casts=true +sp_cleanup.remove_unnecessary_nls_tags=true +sp_cleanup.remove_unused_imports=true sp_cleanup.remove_unused_local_variables=false sp_cleanup.remove_unused_private_fields=true sp_cleanup.remove_unused_private_members=false @@ -52,8 +55,10 @@ sp_cleanup.remove_unused_private_methods=true sp_cleanup.remove_unused_private_types=true sp_cleanup.sort_members=false sp_cleanup.sort_members_all=false -sp_cleanup.use_blocks=false +sp_cleanup.use_anonymous_class_creation=false +sp_cleanup.use_blocks=true sp_cleanup.use_blocks_only_for_return_and_throw=false +sp_cleanup.use_lambda=false sp_cleanup.use_parentheses_in_expressions=false sp_cleanup.use_this_for_non_static_field_access=false sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true diff --git a/org.eclipse.jgit.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.test/META-INF/MANIFEST.MF index ea9b81bb1a..67910209d1 100644 --- a/org.eclipse.jgit.test/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.test/META-INF/MANIFEST.MF @@ -8,6 +8,7 @@ Bundle-Vendor: %provider_name Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Import-Package: com.googlecode.javaewah;version="[1.1.6,2.0.0)", + com.jcraft.jsch;version="[0.1.54,0.2.0)", org.eclipse.jgit.api;version="[4.9.0,4.10.0)", org.eclipse.jgit.api.errors;version="[4.9.0,4.10.0)", org.eclipse.jgit.attributes;version="[4.9.0,4.10.0)", 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 ae0b8dd3c2..e687a6ca7f 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 @@ -76,6 +76,7 @@ import org.eclipse.jgit.submodule.SubmoduleStatusType; import org.eclipse.jgit.submodule.SubmoduleWalk; import org.eclipse.jgit.transport.RefSpec; import org.eclipse.jgit.transport.RemoteConfig; +import org.eclipse.jgit.transport.URIish; import org.eclipse.jgit.util.SystemReader; import org.junit.Test; @@ -145,16 +146,36 @@ public class CloneCommandTest extends RepositoryTestCase { File directory = createTempDirectory("testCloneRepository"); CloneCommand command = Git.cloneRepository(); command.setDirectory(directory); - command.setGitDir(new File(directory, ".git")); + command.setGitDir(new File(directory, Constants.DOT_GIT)); command.setURI(fileUri()); Git git2 = command.call(); addRepoToClose(git2.getRepository()); assertEquals(directory, git2.getRepository().getWorkTree()); - assertEquals(new File(directory, ".git"), git2.getRepository() + assertEquals(new File(directory, Constants.DOT_GIT), git2.getRepository() .getDirectory()); } @Test + public void testCloneRepositoryDefaultDirectory() + throws URISyntaxException, JGitInternalException { + CloneCommand command = Git.cloneRepository().setURI(fileUri()); + + command.verifyDirectories(new URIish(fileUri())); + File directory = command.getDirectory(); + assertEquals(git.getRepository().getWorkTree().getName(), directory.getName()); + } + + @Test + public void testCloneBareRepositoryDefaultDirectory() + throws URISyntaxException, JGitInternalException { + CloneCommand command = Git.cloneRepository().setURI(fileUri()).setBare(true); + + command.verifyDirectories(new URIish(fileUri())); + File directory = command.getDirectory(); + assertEquals(git.getRepository().getWorkTree().getName() + Constants.DOT_GIT_EXT, directory.getName()); + } + + @Test public void testCloneRepositoryExplicitGitDirNonStd() throws IOException, JGitInternalException, GitAPIException { File directory = createTempDirectory("testCloneRepository"); @@ -168,8 +189,8 @@ public class CloneCommandTest extends RepositoryTestCase { assertEquals(directory, git2.getRepository().getWorkTree()); assertEquals(gDir, git2.getRepository() .getDirectory()); - assertTrue(new File(directory, ".git").isFile()); - assertFalse(new File(gDir, ".git").exists()); + assertTrue(new File(directory, Constants.DOT_GIT).isFile()); + assertFalse(new File(gDir, Constants.DOT_GIT).exists()); } @Test diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesHandlerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesHandlerTest.java index 50d020c57c..5868482c88 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesHandlerTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesHandlerTest.java @@ -1,4 +1,7 @@ /* + * Copyright (C) 2015, 2017 Ivan Motsch <ivan.motsch@bsiag.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 @@ -259,25 +262,274 @@ public class AttributesHandlerTest extends RepositoryTestCase { setupRepo("sub/ global", "sub/** init", "sub/** top_sub\n*.txt top", "sub/** subsub\nsub/ subsub2\n*.txt foo"); - // The last two sub/** and sub/ rules are in sub/.gitattributes. They - // must not apply to any of the files here. They would match for a - // further subdirectory sub/sub. + // The last sub/** is in sub/.gitattributes. It must not + // apply to any of the files here. It would match for a + // further subdirectory sub/sub. The sub/ rules must match + // only for directories. walk = beginWalk(); assertIteration(F, ".gitattributes"); assertIteration(D, "sub", attrs("global")); - assertIteration(F, "sub/.gitattributes", attrs("init top_sub global")); - assertIteration(F, "sub/a.txt", attrs("init foo top top_sub global")); + assertIteration(F, "sub/.gitattributes", attrs("init top_sub")); + assertIteration(F, "sub/a.txt", attrs("init foo top top_sub")); endWalk(); // All right, let's see that they *do* apply in sub/sub: writeTrashFile("sub/sub/b.txt", "b"); walk = beginWalk(); assertIteration(F, ".gitattributes"); assertIteration(D, "sub", attrs("global")); - assertIteration(F, "sub/.gitattributes", attrs("init top_sub global")); - assertIteration(F, "sub/a.txt", attrs("init foo top top_sub global")); + assertIteration(F, "sub/.gitattributes", attrs("init top_sub")); + assertIteration(F, "sub/a.txt", attrs("init foo top top_sub")); assertIteration(D, "sub/sub", attrs("init subsub2 top_sub global")); assertIteration(F, "sub/sub/b.txt", - attrs("init foo subsub2 subsub top top_sub global")); + attrs("init foo subsub top top_sub")); + endWalk(); + } + + @Test + public void testNestedMatchNot() throws Exception { + setupRepo(null, null, "*.xml xml\n*.jar jar", null); + writeTrashFile("foo.xml/bar.jar", "b"); + writeTrashFile("foo.xml/bar.xml", "bx"); + writeTrashFile("sub/b.jar", "bj"); + writeTrashFile("sub/b.xml", "bx"); + // On foo.xml/bar.jar we must not have 'xml' + walk = beginWalk(); + assertIteration(F, ".gitattributes"); + assertIteration(D, "foo.xml", attrs("xml")); + assertIteration(F, "foo.xml/bar.jar", attrs("jar")); + assertIteration(F, "foo.xml/bar.xml", attrs("xml")); + assertIteration(D, "sub"); + assertIteration(F, "sub/a.txt"); + assertIteration(F, "sub/b.jar", attrs("jar")); + assertIteration(F, "sub/b.xml", attrs("xml")); + endWalk(); + } + + @Test + public void testNestedMatch() throws Exception { + // See also CGitAttributeTest.testNestedMatch() + setupRepo(null, null, "foo/ xml\nsub/foo/ sub\n*.jar jar", null); + writeTrashFile("foo/bar.jar", "b"); + writeTrashFile("foo/bar.xml", "bx"); + writeTrashFile("sub/b.jar", "bj"); + writeTrashFile("sub/b.xml", "bx"); + writeTrashFile("sub/foo/b.jar", "bf"); + walk = beginWalk(); + assertIteration(F, ".gitattributes"); + assertIteration(D, "foo", attrs("xml")); + assertIteration(F, "foo/bar.jar", attrs("jar")); + assertIteration(F, "foo/bar.xml"); + assertIteration(D, "sub"); + assertIteration(F, "sub/a.txt"); + assertIteration(F, "sub/b.jar", attrs("jar")); + assertIteration(F, "sub/b.xml"); + assertIteration(D, "sub/foo", attrs("sub xml")); + assertIteration(F, "sub/foo/b.jar", attrs("jar")); + endWalk(); + } + + @Test + public void testNestedMatchRecursive() throws Exception { + setupRepo(null, null, "foo/** xml\n*.jar jar", null); + writeTrashFile("foo/bar.jar", "b"); + writeTrashFile("foo/bar.xml", "bx"); + writeTrashFile("sub/b.jar", "bj"); + writeTrashFile("sub/b.xml", "bx"); + writeTrashFile("sub/foo/b.jar", "bf"); + // On foo.xml/bar.jar we must not have 'xml' + walk = beginWalk(); + assertIteration(F, ".gitattributes"); + assertIteration(D, "foo"); + assertIteration(F, "foo/bar.jar", attrs("jar xml")); + assertIteration(F, "foo/bar.xml", attrs("xml")); + assertIteration(D, "sub"); + assertIteration(F, "sub/a.txt"); + assertIteration(F, "sub/b.jar", attrs("jar")); + assertIteration(F, "sub/b.xml"); + assertIteration(D, "sub/foo"); + assertIteration(F, "sub/foo/b.jar", attrs("jar")); + endWalk(); + } + + @Test + public void testStarMatchOnSlashNot() throws Exception { + setupRepo(null, null, "s*xt bar", null); + writeTrashFile("sub/a.txt", "1"); + writeTrashFile("foo/sext", "2"); + writeTrashFile("foo/s.txt", "3"); + walk = beginWalk(); + assertIteration(F, ".gitattributes"); + assertIteration(D, "foo"); + assertIteration(F, "foo/s.txt", attrs("bar")); + assertIteration(F, "foo/sext", attrs("bar")); + assertIteration(D, "sub"); + assertIteration(F, "sub/a.txt"); + endWalk(); + } + + @Test + public void testPrefixMatchNot() throws Exception { + setupRepo(null, null, "sub/new bar", null); + writeTrashFile("sub/new/foo.txt", "1"); + walk = beginWalk(); + assertIteration(F, ".gitattributes"); + assertIteration(D, "sub"); + assertIteration(F, "sub/a.txt"); + assertIteration(D, "sub/new", attrs("bar")); + assertIteration(F, "sub/new/foo.txt"); + endWalk(); + } + + @Test + public void testComplexPathMatch() throws Exception { + setupRepo(null, null, "s[t-v]b/n[de]w bar", null); + writeTrashFile("sub/new/foo.txt", "1"); + writeTrashFile("sub/ndw", "2"); + walk = beginWalk(); + assertIteration(F, ".gitattributes"); + assertIteration(D, "sub"); + assertIteration(F, "sub/a.txt"); + assertIteration(F, "sub/ndw", attrs("bar")); + assertIteration(D, "sub/new", attrs("bar")); + assertIteration(F, "sub/new/foo.txt"); + endWalk(); + } + + @Test + public void testStarPathMatch() throws Exception { + setupRepo(null, null, "sub/new/* bar", null); + writeTrashFile("sub/new/foo.txt", "1"); + writeTrashFile("sub/new/lower/foo.txt", "2"); + walk = beginWalk(); + assertIteration(F, ".gitattributes"); + assertIteration(D, "sub"); + assertIteration(F, "sub/a.txt"); + assertIteration(D, "sub/new"); + assertIteration(F, "sub/new/foo.txt", attrs("bar")); + assertIteration(D, "sub/new/lower", attrs("bar")); + assertIteration(F, "sub/new/lower/foo.txt"); + endWalk(); + } + + @Test + public void testDirectoryMatchSubSimple() throws Exception { + setupRepo(null, null, "sub/new/ bar", null); + writeTrashFile("sub/new/foo.txt", "1"); + writeTrashFile("foo/sub/new/foo.txt", "2"); + writeTrashFile("sub/sub/new/foo.txt", "3"); + walk = beginWalk(); + assertIteration(F, ".gitattributes"); + assertIteration(D, "foo"); + assertIteration(D, "foo/sub"); + assertIteration(D, "foo/sub/new"); + assertIteration(F, "foo/sub/new/foo.txt"); + assertIteration(D, "sub"); + assertIteration(F, "sub/a.txt"); + assertIteration(D, "sub/new", attrs("bar")); + assertIteration(F, "sub/new/foo.txt"); + assertIteration(D, "sub/sub"); + assertIteration(D, "sub/sub/new"); + assertIteration(F, "sub/sub/new/foo.txt"); + endWalk(); + } + + @Test + public void testDirectoryMatchSubRecursive() throws Exception { + setupRepo(null, null, "**/sub/new/ bar", null); + writeTrashFile("sub/new/foo.txt", "1"); + writeTrashFile("foo/sub/new/foo.txt", "2"); + walk = beginWalk(); + assertIteration(F, ".gitattributes"); + assertIteration(D, "foo"); + assertIteration(D, "foo/sub"); + assertIteration(D, "foo/sub/new", attrs("bar")); + assertIteration(F, "foo/sub/new/foo.txt"); + assertIteration(D, "sub"); + assertIteration(F, "sub/a.txt"); + assertIteration(D, "sub/new", attrs("bar")); + assertIteration(F, "sub/new/foo.txt"); + endWalk(); + } + + @Test + public void testDirectoryMatchSubRecursiveBacktrack() throws Exception { + setupRepo(null, null, "**/sub/new/ bar", null); + writeTrashFile("sub/new/foo.txt", "1"); + writeTrashFile("foo/sub/new/foo.txt", "2"); + writeTrashFile("sub/sub/new/foo.txt", "3"); + walk = beginWalk(); + assertIteration(F, ".gitattributes"); + assertIteration(D, "foo"); + assertIteration(D, "foo/sub"); + assertIteration(D, "foo/sub/new", attrs("bar")); + assertIteration(F, "foo/sub/new/foo.txt"); + assertIteration(D, "sub"); + assertIteration(F, "sub/a.txt"); + assertIteration(D, "sub/new", attrs("bar")); + assertIteration(F, "sub/new/foo.txt"); + assertIteration(D, "sub/sub"); + assertIteration(D, "sub/sub/new", attrs("bar")); + assertIteration(F, "sub/sub/new/foo.txt"); + endWalk(); + } + + @Test + public void testDirectoryMatchSubRecursiveBacktrack2() throws Exception { + setupRepo(null, null, "**/**/sub/new/ bar", null); + writeTrashFile("sub/new/foo.txt", "1"); + writeTrashFile("foo/sub/new/foo.txt", "2"); + writeTrashFile("sub/sub/new/foo.txt", "3"); + walk = beginWalk(); + assertIteration(F, ".gitattributes"); + assertIteration(D, "foo"); + assertIteration(D, "foo/sub"); + assertIteration(D, "foo/sub/new", attrs("bar")); + assertIteration(F, "foo/sub/new/foo.txt"); + assertIteration(D, "sub"); + assertIteration(F, "sub/a.txt"); + assertIteration(D, "sub/new", attrs("bar")); + assertIteration(F, "sub/new/foo.txt"); + assertIteration(D, "sub/sub"); + assertIteration(D, "sub/sub/new", attrs("bar")); + assertIteration(F, "sub/sub/new/foo.txt"); + endWalk(); + } + + @Test + public void testDirectoryMatchSubComplex() throws Exception { + setupRepo(null, null, "s[uv]b/n*/ bar", null); + writeTrashFile("sub/new/foo.txt", "1"); + writeTrashFile("foo/sub/new/foo.txt", "2"); + walk = beginWalk(); + assertIteration(F, ".gitattributes"); + assertIteration(D, "foo"); + assertIteration(D, "foo/sub"); + assertIteration(D, "foo/sub/new"); + assertIteration(F, "foo/sub/new/foo.txt"); + assertIteration(D, "sub"); + assertIteration(F, "sub/a.txt"); + assertIteration(D, "sub/new", attrs("bar")); + assertIteration(F, "sub/new/foo.txt"); + endWalk(); + } + + @Test + public void testDirectoryMatch() throws Exception { + setupRepo(null, null, "new/ bar", null); + writeTrashFile("sub/new/foo.txt", "1"); + writeTrashFile("foo/sub/new/foo.txt", "2"); + writeTrashFile("foo/new", "3"); + walk = beginWalk(); + assertIteration(F, ".gitattributes"); + assertIteration(D, "foo"); + assertIteration(F, "foo/new"); + assertIteration(D, "foo/sub"); + assertIteration(D, "foo/sub/new", attrs("bar")); + assertIteration(F, "foo/sub/new/foo.txt"); + assertIteration(D, "sub"); + assertIteration(F, "sub/a.txt"); + assertIteration(D, "sub/new", attrs("bar")); + assertIteration(F, "sub/new/foo.txt"); endWalk(); } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesMatcherTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesMatcherTest.java index e8dd952324..23c416a45b 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesMatcherTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesMatcherTest.java @@ -109,16 +109,16 @@ public class AttributesMatcherTest { pattern = "/src/ne?"; assertMatched(pattern, "/src/new/"); assertMatched(pattern, "/src/new"); - assertMatched(pattern, "/src/new/a.c"); - assertMatched(pattern, "/src/new/a/a.c"); + assertNotMatched(pattern, "/src/new/a.c"); + assertNotMatched(pattern, "/src/new/a/a.c"); assertNotMatched(pattern, "/src/new.c"); //Test name-only fnmatcher matches pattern = "ne?"; assertMatched(pattern, "/src/new/"); assertMatched(pattern, "/src/new"); - assertMatched(pattern, "/src/new/a.c"); - assertMatched(pattern, "/src/new/a/a.c"); + assertNotMatched(pattern, "/src/new/a.c"); + assertNotMatched(pattern, "/src/new/a/a.c"); assertMatched(pattern, "/neb"); assertNotMatched(pattern, "/src/new.c"); } @@ -169,16 +169,16 @@ public class AttributesMatcherTest { pattern = "/src/ne?"; assertMatched(pattern, "src/new/"); assertMatched(pattern, "src/new"); - assertMatched(pattern, "src/new/a.c"); - assertMatched(pattern, "src/new/a/a.c"); + assertNotMatched(pattern, "src/new/a.c"); + assertNotMatched(pattern, "src/new/a/a.c"); assertNotMatched(pattern, "src/new.c"); //Test name-only fnmatcher matches pattern = "ne?"; assertMatched(pattern, "src/new/"); assertMatched(pattern, "src/new"); - assertMatched(pattern, "src/new/a.c"); - assertMatched(pattern, "src/new/a/a.c"); + assertNotMatched(pattern, "src/new/a.c"); + assertNotMatched(pattern, "src/new/a/a.c"); assertMatched(pattern, "neb"); assertNotMatched(pattern, "src/new.c"); } @@ -197,35 +197,50 @@ public class AttributesMatcherTest { pattern = "/src/new"; assertMatched(pattern, "/src/new/"); assertMatched(pattern, "/src/new"); - assertMatched(pattern, "/src/new/a.c"); - assertMatched(pattern, "/src/new/a/a.c"); + assertNotMatched(pattern, "/src/new/a.c"); + assertNotMatched(pattern, "/src/new/a/a.c"); assertNotMatched(pattern, "/src/new.c"); //Test child directory is matched, slash after name pattern = "/src/new/"; assertMatched(pattern, "/src/new/"); - assertMatched(pattern, "/src/new/a.c"); - assertMatched(pattern, "/src/new/a/a.c"); + assertNotMatched(pattern, "/src/new/a.c"); + assertNotMatched(pattern, "/src/new/a/a.c"); assertNotMatched(pattern, "/src/new"); assertNotMatched(pattern, "/src/new.c"); //Test directory is matched by name only pattern = "b1"; - assertMatched(pattern, "/src/new/a/b1/a.c"); + assertNotMatched(pattern, "/src/new/a/b1/a.c"); assertNotMatched(pattern, "/src/new/a/b2/file.c"); assertNotMatched(pattern, "/src/new/a/bb1/file.c"); assertNotMatched(pattern, "/src/new/a/file.c"); + assertNotMatched(pattern, "/src/new/a/bb1"); + assertMatched(pattern, "/src/new/a/b1"); } @Test public void testTrailingSlash() { String pattern = "/src/"; assertMatched(pattern, "/src/"); - assertMatched(pattern, "/src/new"); - assertMatched(pattern, "/src/new/a.c"); - assertMatched(pattern, "/src/a.c"); + assertNotMatched(pattern, "/src/new"); + assertNotMatched(pattern, "/src/new/a.c"); + assertNotMatched(pattern, "/src/a.c"); assertNotMatched(pattern, "/src"); assertNotMatched(pattern, "/srcA/"); + + pattern = "src/"; + assertMatched(pattern, "src/"); + assertMatched(pattern, "/src/"); + assertNotMatched(pattern, "src"); + assertNotMatched(pattern, "/src/new"); + assertNotMatched(pattern, "/src/new/a.c"); + assertNotMatched(pattern, "/src/a.c"); + assertNotMatched(pattern, "foo/src/a.c"); + assertNotMatched(pattern, "foo/src/bar/a.c"); + assertNotMatched(pattern, "foo/src/bar/src"); + assertMatched(pattern, "foo/src/"); + assertMatched(pattern, "foo/src/bar/src/"); } @Test @@ -239,51 +254,58 @@ public class AttributesMatcherTest { assertMatched(pattern, "/src/test.stp"); assertNotMatched(pattern, "/test.stp1"); assertNotMatched(pattern, "/test.astp"); + assertNotMatched(pattern, "test.stp/foo.bar"); + assertMatched(pattern, "test.stp"); + assertMatched(pattern, "test.stp/"); + assertMatched(pattern, "test.stp/test.stp"); //Test matches for name-only, applies to file name or folder name pattern = "src"; assertMatched(pattern, "/src"); assertMatched(pattern, "/src/"); - assertMatched(pattern, "/src/a.c"); - assertMatched(pattern, "/src/new/a.c"); - assertMatched(pattern, "/new/src/a.c"); + assertNotMatched(pattern, "/src/a.c"); + assertNotMatched(pattern, "/src/new/a.c"); + assertNotMatched(pattern, "/new/src/a.c"); assertMatched(pattern, "/file/src"); //Test matches for name-only, applies only to folder names pattern = "src/"; - assertMatched(pattern, "/src/"); - assertMatched(pattern, "/src/a.c"); - assertMatched(pattern, "/src/new/a.c"); - assertMatched(pattern, "/new/src/a.c"); + assertNotMatched(pattern, "/src/a.c"); + assertNotMatched(pattern, "/src/new/a.c"); + assertNotMatched(pattern, "/new/src/a.c"); assertNotMatched(pattern, "/src"); assertNotMatched(pattern, "/file/src"); + assertMatched(pattern, "/file/src/"); //Test matches for name-only, applies to file name or folder name //With a small wildcard pattern = "?rc"; - assertMatched(pattern, "/src/a.c"); - assertMatched(pattern, "/src/new/a.c"); - assertMatched(pattern, "/new/src/a.c"); + assertNotMatched(pattern, "/src/a.c"); + assertNotMatched(pattern, "/src/new/a.c"); + assertNotMatched(pattern, "/new/src/a.c"); + assertMatched(pattern, "/new/src/"); assertMatched(pattern, "/file/src"); assertMatched(pattern, "/src/"); //Test matches for name-only, applies to file name or folder name //With a small wildcard pattern = "?r[a-c]"; - assertMatched(pattern, "/src/a.c"); - assertMatched(pattern, "/src/new/a.c"); - assertMatched(pattern, "/new/src/a.c"); + assertNotMatched(pattern, "/src/a.c"); + assertNotMatched(pattern, "/src/new/a.c"); + assertNotMatched(pattern, "/new/src/a.c"); assertMatched(pattern, "/file/src"); assertMatched(pattern, "/src/"); - assertMatched(pattern, "/srb/a.c"); - assertMatched(pattern, "/grb/new/a.c"); - assertMatched(pattern, "/new/crb/a.c"); + assertNotMatched(pattern, "/srb/a.c"); + assertNotMatched(pattern, "/grb/new/a.c"); + assertNotMatched(pattern, "/new/crb/a.c"); assertMatched(pattern, "/file/3rb"); assertMatched(pattern, "/xrb/"); - assertMatched(pattern, "/3ra/a.c"); - assertMatched(pattern, "/5ra/new/a.c"); - assertMatched(pattern, "/new/1ra/a.c"); + assertNotMatched(pattern, "/3ra/a.c"); + assertNotMatched(pattern, "/5ra/new/a.c"); + assertNotMatched(pattern, "/new/1ra/a.c"); + assertNotMatched(pattern, "/new/1ra/a.c/"); assertMatched(pattern, "/file/dra"); + assertMatched(pattern, "/file/dra/"); assertMatched(pattern, "/era/"); assertNotMatched(pattern, "/crg"); assertNotMatched(pattern, "/cr3"); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/CGitAttributesTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/CGitAttributesTest.java new file mode 100644 index 0000000000..6188fa60c9 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/CGitAttributesTest.java @@ -0,0 +1,380 @@ +/* + * Copyright (C) 2017 Thomas Wolf <thomas.wolf@paranor.ch> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.eclipse.jgit.attributes; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; + +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; + +import org.eclipse.jgit.junit.RepositoryTestCase; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.StoredConfig; +import org.eclipse.jgit.treewalk.FileTreeIterator; +import org.eclipse.jgit.treewalk.TreeWalk; +import org.eclipse.jgit.treewalk.filter.NotIgnoredFilter; +import org.eclipse.jgit.util.FS; +import org.eclipse.jgit.util.FS.ExecutionResult; +import org.eclipse.jgit.util.RawParseUtils; +import org.eclipse.jgit.util.TemporaryBuffer; +import org.junit.Before; +import org.junit.Test; + +/** + * Tests that verify that the attributes of files in a repository are the same + * in JGit and in C-git. + */ +public class CGitAttributesTest extends RepositoryTestCase { + + @Before + public void initRepo() throws IOException { + // Because we run C-git, we must ensure that global or user exclude + // files cannot influence the tests. So we set core.excludesFile to an + // empty file inside the repository. + StoredConfig config = db.getConfig(); + File fakeUserGitignore = writeTrashFile(".fake_user_gitignore", ""); + config.setString("core", null, "excludesFile", + fakeUserGitignore.getAbsolutePath()); + // Disable case-insensitivity -- JGit doesn't handle that yet. + config.setBoolean("core", null, "ignoreCase", false); + // And try to switch off the global attributes file, too. + config.setString("core", null, "attributesFile", + fakeUserGitignore.getAbsolutePath()); + config.save(); + } + + private void createFiles(String... paths) throws IOException { + for (String path : paths) { + writeTrashFile(path, "x"); + } + } + + private String toString(TemporaryBuffer b) throws IOException { + return RawParseUtils.decode(b.toByteArray()); + } + + private Attribute fromString(String key, String value) { + if ("set".equals(value)) { + return new Attribute(key, Attribute.State.SET); + } + if ("unset".equals(value)) { + return new Attribute(key, Attribute.State.UNSET); + } + if ("unspecified".equals(value)) { + return new Attribute(key, Attribute.State.UNSPECIFIED); + } + return new Attribute(key, value); + } + + private LinkedHashMap<String, Attributes> cgitAttributes( + Set<String> allFiles) throws Exception { + FS fs = db.getFS(); + StringBuilder input = new StringBuilder(); + for (String filename : allFiles) { + input.append(filename).append('\n'); + } + ProcessBuilder builder = fs.runInShell("git", + new String[] { "check-attr", "--stdin", "--all" }); + builder.directory(db.getWorkTree()); + ExecutionResult result = fs.execute(builder, new ByteArrayInputStream( + input.toString().getBytes(Constants.CHARSET))); + assertEquals("External git reported errors", "", + toString(result.getStderr())); + assertEquals("External git failed", 0, result.getRc()); + LinkedHashMap<String, Attributes> map = new LinkedHashMap<>(); + try (BufferedReader r = new BufferedReader(new InputStreamReader( + new BufferedInputStream(result.getStdout().openInputStream()), + Constants.CHARSET))) { + r.lines().forEach(line -> { + // Parse the line and add to result map + int start = 0; + int i = line.indexOf(':'); + String path = line.substring(0, i).trim(); + start = i + 1; + i = line.indexOf(':', start); + String key = line.substring(start, i).trim(); + String value = line.substring(i + 1).trim(); + Attribute attr = fromString(key, value); + Attributes attrs = map.get(path); + if (attrs == null) { + attrs = new Attributes(attr); + map.put(path, attrs); + } else { + attrs.put(attr); + } + }); + } + return map; + } + + private LinkedHashMap<String, Attributes> jgitAttributes() + throws IOException { + // Do a tree walk and return a list of all files and directories with + // their attributes + LinkedHashMap<String, Attributes> result = new LinkedHashMap<>(); + try (TreeWalk walk = new TreeWalk(db)) { + walk.addTree(new FileTreeIterator(db)); + walk.setFilter(new NotIgnoredFilter(0)); + while (walk.next()) { + String path = walk.getPathString(); + if (walk.isSubtree() && !path.endsWith("/")) { + // git check-attr expects directory paths to end with a + // slash + path += '/'; + } + Attributes attrs = walk.getAttributes(); + if (attrs != null && !attrs.isEmpty()) { + result.put(path, attrs); + } else { + result.put(path, null); + } + if (walk.isSubtree()) { + walk.enterSubtree(); + } + } + } + return result; + } + + private void assertSameAsCGit() throws Exception { + LinkedHashMap<String, Attributes> jgit = jgitAttributes(); + LinkedHashMap<String, Attributes> cgit = cgitAttributes(jgit.keySet()); + // remove all without attributes + Iterator<Map.Entry<String, Attributes>> iterator = jgit.entrySet() + .iterator(); + while (iterator.hasNext()) { + Map.Entry<String, Attributes> entry = iterator.next(); + if (entry.getValue() == null) { + iterator.remove(); + } + } + assertArrayEquals("JGit attributes differ from C git", + cgit.entrySet().toArray(), jgit.entrySet().toArray()); + } + + @Test + public void testBug508568() throws Exception { + createFiles("foo.xml/bar.jar", "sub/foo.xml/bar.jar"); + writeTrashFile(".gitattributes", "*.xml xml\n" + "*.jar jar\n"); + assertSameAsCGit(); + } + + @Test + public void testRelativePath() throws Exception { + createFiles("sub/foo.txt"); + writeTrashFile("sub/.gitattributes", "sub/** sub\n" + "*.txt txt\n"); + assertSameAsCGit(); + } + + @Test + public void testRelativePaths() throws Exception { + createFiles("sub/foo.txt", "sub/sub/bar", "foo/sub/a.txt", + "foo/sub/bar/a.tmp"); + writeTrashFile(".gitattributes", "sub/** sub\n" + "*.txt txt\n"); + assertSameAsCGit(); + } + + @Test + public void testNestedMatchNot() throws Exception { + createFiles("foo.xml/bar.jar", "foo.xml/bar.xml", "sub/b.jar", + "sub/b.xml"); + writeTrashFile("sub/.gitattributes", "*.xml xml\n" + "*.jar jar\n"); + assertSameAsCGit(); + } + + @Test + public void testNestedMatch() throws Exception { + // This is an interesting test. At the time of this writing, the + // gitignore documentation says: "In other words, foo/ will match a + // directory foo AND PATHS UNDERNEATH IT, but will not match a regular + // file or a symbolic link foo". (Emphasis added.) And gitattributes is + // supposed to follow the same rules. But the documentation appears to + // lie: C-git will *not* apply the attribute "xml" to *any* files in + // any subfolder "foo" here. It will only apply the "jar" attribute + // to the three *.jar files. + // + // The point is probably that ignores are handled top-down, and once a + // directory "foo" is matched (here: on paths "foo" and "sub/foo" by + // pattern "foo/"), the directory is excluded and the gitignore + // documentation also says: "It is not possible to re-include a file if + // a parent directory of that file is excluded." So once the pattern + // "foo/" has matched, it appears as if everything beneath would also be + // matched. + // + // But not so for gitattributes! The foo/ rule only matches the + // directory itself, but not anything beneath. + createFiles("foo/bar.jar", "foo/bar.xml", "sub/b.jar", "sub/b.xml", + "sub/foo/b.jar"); + writeTrashFile(".gitattributes", + "foo/ xml\n" + "sub/foo/ sub\n" + "*.jar jar\n"); + assertSameAsCGit(); + } + + @Test + public void testNestedMatchWithWildcard() throws Exception { + // See above. + createFiles("foo/bar.jar", "foo/bar.xml", "sub/b.jar", "sub/b.xml", + "sub/foo/b.jar"); + writeTrashFile(".gitattributes", + "**/foo/ xml\n" + "*/foo/ sub\n" + "*.jar jar\n"); + assertSameAsCGit(); + } + + @Test + public void testNestedMatchRecursive() throws Exception { + createFiles("foo/bar.jar", "foo/bar.xml", "sub/b.jar", "sub/b.xml", + "sub/foo/b.jar"); + writeTrashFile(".gitattributes", "foo/** xml\n" + "*.jar jar\n"); + assertSameAsCGit(); + } + + @Test + public void testStarMatchOnSlashNot() throws Exception { + createFiles("sub/a.txt", "foo/sext", "foo/s.txt"); + writeTrashFile(".gitattributes", "s*xt bar"); + assertSameAsCGit(); + } + + @Test + public void testPrefixMatchNot() throws Exception { + createFiles("src/new/foo.txt"); + writeTrashFile(".gitattributes", "src/new bar\n"); + assertSameAsCGit(); + } + + @Test + public void testComplexPathMatchNot() throws Exception { + createFiles("src/new/foo.txt", "src/ndw"); + writeTrashFile(".gitattributes", "s[p-s]c/n[de]w bar\n"); + assertSameAsCGit(); + } + + @Test + public void testStarPathMatchNot() throws Exception { + createFiles("src/new/foo.txt", "src/ndw"); + writeTrashFile(".gitattributes", "src/* bar\n"); + assertSameAsCGit(); + } + + @Test + public void testDirectoryMatchSubSimple() throws Exception { + createFiles("src/new/foo.txt", "foo/src/new/foo.txt", "sub/src/new"); + writeTrashFile(".gitattributes", "src/new/ bar\n"); + assertSameAsCGit(); + } + + @Test + public void testDirectoryMatchSubRecursive() throws Exception { + createFiles("src/new/foo.txt", "foo/src/new/foo.txt", "sub/src/new"); + writeTrashFile(".gitattributes", "**/src/new/ bar\n"); + assertSameAsCGit(); + } + + @Test + public void testDirectoryMatchSubRecursiveBacktrack() throws Exception { + createFiles("src/new/foo.txt", "src/src/new/foo.txt"); + writeTrashFile(".gitattributes", "**/src/new/ bar\n"); + assertSameAsCGit(); + } + + @Test + public void testDirectoryMatchSubRecursiveBacktrack2() throws Exception { + createFiles("src/new/foo.txt", "src/src/new/foo.txt"); + writeTrashFile(".gitattributes", "**/**/src/new/ bar\n"); + assertSameAsCGit(); + } + + @Test + public void testDirectoryMatchSubRecursiveBacktrack3() throws Exception { + createFiles("src/new/src/new/foo.txt", + "foo/src/new/bar/src/new/foo.txt"); + writeTrashFile(".gitattributes", "**/src/new/ bar\n"); + assertSameAsCGit(); + } + + @Test + public void testDirectoryMatchSubRecursiveBacktrack4() throws Exception { + createFiles("src/src/src/new/foo.txt", + "foo/src/src/bar/src/new/foo.txt"); + writeTrashFile(".gitattributes", "**/src/ bar\n"); + assertSameAsCGit(); + } + + @Test + public void testDirectoryMatchSubRecursiveBacktrack5() throws Exception { + createFiles("x/a/a/b/foo.txt", "x/y/z/b/a/b/foo.txt", + "x/y/a/a/a/a/b/foo.txt", "x/y/a/a/a/a/b/a/b/foo.txt"); + writeTrashFile(".gitattributes", "**/*/a/b bar\n"); + assertSameAsCGit(); + } + + @Test + public void testDirectoryMatchSubRecursiveBacktrack6() throws Exception { + createFiles("x/a/a/b/foo.txt", "x/y/a/b/a/b/foo.txt"); + writeTrashFile(".gitattributes", "**/*/**/a/b bar\n"); + assertSameAsCGit(); + } + + @Test + public void testDirectoryMatchSubComplex() throws Exception { + createFiles("src/new/foo.txt", "foo/src/new/foo.txt", "sub/src/new"); + writeTrashFile(".gitattributes", "s[rs]c/n*/ bar\n"); + assertSameAsCGit(); + } + + @Test + public void testDirectoryMatch() throws Exception { + createFiles("src/new/foo.txt", "foo/src/new/foo.txt", "sub/src/new"); + writeTrashFile(".gitattributes", "new/ bar\n"); + assertSameAsCGit(); + } +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/CGitIgnoreTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/CGitIgnoreTest.java new file mode 100644 index 0000000000..c61f2ea7f6 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/CGitIgnoreTest.java @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2017 Thomas Wolf <thomas.wolf@paranor.ch> + * 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.ignore; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.LinkedHashSet; +import java.util.Set; + +import org.eclipse.jgit.junit.RepositoryTestCase; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.StoredConfig; +import org.eclipse.jgit.treewalk.FileTreeIterator; +import org.eclipse.jgit.treewalk.TreeWalk; +import org.eclipse.jgit.treewalk.WorkingTreeIterator; +import org.eclipse.jgit.treewalk.filter.NotIgnoredFilter; +import org.eclipse.jgit.util.FS; +import org.eclipse.jgit.util.FS.ExecutionResult; +import org.eclipse.jgit.util.RawParseUtils; +import org.eclipse.jgit.util.TemporaryBuffer; +import org.junit.Before; +import org.junit.Test; + +/** + * Tests that verify that the set of ignore files in a repository is the same in + * JGit and in C-git. + */ +public class CGitIgnoreTest extends RepositoryTestCase { + + @Before + public void initRepo() throws IOException { + // These tests focus on .gitignore files inside the repository. Because + // we run C-git, we must ensure that global or user exclude files cannot + // influence the tests. So we set core.excludesFile to an empty file + // inside the repository. + File fakeUserGitignore = writeTrashFile(".fake_user_gitignore", ""); + StoredConfig config = db.getConfig(); + config.setString("core", null, "excludesFile", + fakeUserGitignore.getAbsolutePath()); + // Disable case-insensitivity -- JGit doesn't handle that yet. + config.setBoolean("core", null, "ignoreCase", false); + config.save(); + } + + private void createFiles(String... paths) throws IOException { + for (String path : paths) { + writeTrashFile(path, "x"); + } + } + + private String toString(TemporaryBuffer b) throws IOException { + return RawParseUtils.decode(b.toByteArray()); + } + + private String[] cgitIgnored() throws Exception { + FS fs = db.getFS(); + ProcessBuilder builder = fs.runInShell("git", new String[] { "ls-files", + "--ignored", "--exclude-standard", "-o" }); + builder.directory(db.getWorkTree()); + ExecutionResult result = fs.execute(builder, + new ByteArrayInputStream(new byte[0])); + assertEquals("External git failed", 0, result.getRc()); + assertEquals("External git reported errors", "", + toString(result.getStderr())); + try (BufferedReader r = new BufferedReader(new InputStreamReader( + new BufferedInputStream(result.getStdout().openInputStream()), + Constants.CHARSET))) { + return r.lines().toArray(String[]::new); + } + } + + private LinkedHashSet<String> jgitIgnored() throws IOException { + // Do a tree walk that does descend into ignored directories and return + // a list of all ignored files + LinkedHashSet<String> result = new LinkedHashSet<>(); + try (TreeWalk walk = new TreeWalk(db)) { + walk.addTree(new FileTreeIterator(db)); + walk.setRecursive(true); + while (walk.next()) { + if (walk.getTree(WorkingTreeIterator.class).isEntryIgnored()) { + result.add(walk.getPathString()); + } + } + } + return result; + } + + private void assertNoIgnoredVisited(Set<String> ignored) throws Exception { + // Do a recursive tree walk with a NotIgnoredFilter and verify that none + // of the files visited is in the ignored set + try (TreeWalk walk = new TreeWalk(db)) { + walk.addTree(new FileTreeIterator(db)); + walk.setFilter(new NotIgnoredFilter(0)); + walk.setRecursive(true); + while (walk.next()) { + String path = walk.getPathString(); + assertFalse("File " + path + " is ignored, should not appear", + ignored.contains(path)); + } + } + } + + private void assertSameAsCGit(String... notIgnored) throws Exception { + LinkedHashSet<String> ignored = jgitIgnored(); + String[] cgit = cgitIgnored(); + assertArrayEquals(cgit, ignored.toArray()); + for (String notExcluded : notIgnored) { + assertFalse("File " + notExcluded + " should not be ignored", + ignored.contains(notExcluded)); + } + assertNoIgnoredVisited(ignored); + } + + @Test + public void testSimpleIgnored() throws Exception { + createFiles("a.txt", "a.tmp", "src/sub/a.txt", "src/a.tmp", + "src/a.txt/b.tmp", "ignored/a.tmp", "ignored/not_ignored/a.tmp", + "ignored/other/a.tmp"); + writeTrashFile(".gitignore", + "*.txt\n" + "/ignored/*\n" + "!/ignored/not_ignored"); + assertSameAsCGit("ignored/not_ignored/a.tmp"); + } + + @Test + public void testDirOnlyMatch() throws Exception { + createFiles("a.txt", "src/foo/a.txt", "src/a.txt", "foo/a.txt"); + writeTrashFile(".gitignore", "foo/"); + assertSameAsCGit(); + } + + @Test + public void testDirOnlyMatchDeep() throws Exception { + createFiles("a.txt", "src/foo/a.txt", "src/a.txt", "foo/a.txt"); + writeTrashFile(".gitignore", "**/foo/"); + assertSameAsCGit(); + } + + @Test + public void testStarMatchOnSlashNot() throws Exception { + createFiles("sub/a.txt", "foo/sext", "foo/s.txt"); + writeTrashFile(".gitignore", "s*xt"); + assertSameAsCGit("sub/a.txt"); + } + + @Test + public void testPrefixMatch() throws Exception { + createFiles("src/new/foo.txt"); + writeTrashFile(".gitignore", "src/new"); + assertSameAsCGit(); + } + + @Test + public void testDirectoryMatchSubRecursive() throws Exception { + createFiles("src/new/foo.txt", "foo/src/new/foo.txt", "sub/src/new"); + writeTrashFile(".gitignore", "**/src/new/"); + assertSameAsCGit(); + } + + @Test + public void testDirectoryMatchSubRecursiveBacktrack() throws Exception { + createFiles("src/new/foo.txt", "src/src/new/foo.txt"); + writeTrashFile(".gitignore", "**/src/new/"); + assertSameAsCGit(); + } + + @Test + public void testDirectoryMatchSubRecursiveBacktrack2() throws Exception { + createFiles("src/new/foo.txt", "src/src/new/foo.txt"); + writeTrashFile(".gitignore", "**/**/src/new/"); + assertSameAsCGit(); + } + + @Test + public void testDirectoryMatchSubRecursiveBacktrack3() throws Exception { + createFiles("x/a/a/b/foo.txt"); + writeTrashFile(".gitignore", "**/*/a/b/"); + assertSameAsCGit(); + } + + @Test + public void testDirectoryMatchSubRecursiveBacktrack4() throws Exception { + createFiles("x/a/a/b/foo.txt", "x/y/z/b/a/b/foo.txt", + "x/y/a/a/a/a/b/foo.txt", "x/y/a/a/a/a/b/a/b/foo.txt"); + writeTrashFile(".gitignore", "**/*/a/b bar\n"); + assertSameAsCGit(); + } + + @Test + public void testDirectoryMatchSubRecursiveBacktrack5() throws Exception { + createFiles("x/a/a/b/foo.txt", "x/y/a/b/a/b/foo.txt"); + writeTrashFile(".gitignore", "**/*/**/a/b bar\n"); + assertSameAsCGit(); + } + +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectCheckerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectCheckerTest.java index c8729d9443..7f783536a1 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectCheckerTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectCheckerTest.java @@ -63,6 +63,7 @@ import static org.eclipse.jgit.lib.ObjectChecker.ErrorType.HAS_DOTGIT; import static org.eclipse.jgit.lib.ObjectChecker.ErrorType.NULL_SHA1; import static org.eclipse.jgit.lib.ObjectChecker.ErrorType.TREE_NOT_SORTED; import static org.eclipse.jgit.lib.ObjectChecker.ErrorType.ZERO_PADDED_FILEMODE; +import static org.eclipse.jgit.util.RawParseUtils.decode; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertSame; import static org.junit.Assert.fail; @@ -73,11 +74,16 @@ import java.text.MessageFormat; import org.eclipse.jgit.errors.CorruptObjectException; import org.eclipse.jgit.internal.JGitText; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; public class ObjectCheckerTest { private ObjectChecker checker; + @Rule + public final ExpectedException thrown = ExpectedException.none(); + @Before public void setUp() throws Exception { checker = new ObjectChecker(); @@ -102,6 +108,98 @@ public class ObjectCheckerTest { } @Test + public void testCheckBlobNotCorrupt() throws CorruptObjectException { + checker = new ObjectChecker() { + @Override + public void checkBlob(byte[] raw) throws CorruptObjectException { + String in = decode(raw); + if (in.contains("secret_key")) { + throw new CorruptObjectException("don't add a secret key"); + } + } + }; + checker.check(OBJ_BLOB, encodeASCII("key = \"public_key\"")); + } + + @Test + public void testCheckBlobCorrupt() throws CorruptObjectException { + checker = new ObjectChecker() { + @Override + public void checkBlob(byte[] raw) throws CorruptObjectException { + String in = decode(raw); + if (in.contains("secret_key")) { + throw new CorruptObjectException("don't add a secret key"); + } + } + }; + thrown.expect(CorruptObjectException.class); + checker.check(OBJ_BLOB, encodeASCII("key = \"secret_key\"")); + } + + @Test + public void testCheckBlobWithBlobObjectCheckerNotCorrupt() + throws CorruptObjectException { + checker = new ObjectChecker() { + @Override + public BlobObjectChecker newBlobObjectChecker() { + return new BlobObjectChecker() { + private boolean containSecretKey; + + @Override + public void update(byte[] in, int offset, int len) { + String str = decode(in, offset, offset + len); + if (str.contains("secret_key")) { + containSecretKey = true; + } + } + + @Override + public void endBlob(AnyObjectId id) + throws CorruptObjectException { + if (containSecretKey) { + throw new CorruptObjectException( + "don't add a secret key"); + } + } + }; + } + }; + checker.check(OBJ_BLOB, encodeASCII("key = \"public_key\"")); + } + + @Test + public void testCheckBlobWithBlobObjectCheckerCorrupt() + throws CorruptObjectException { + checker = new ObjectChecker() { + @Override + public BlobObjectChecker newBlobObjectChecker() { + return new BlobObjectChecker() { + private boolean containSecretKey; + + @Override + public void update(byte[] in, int offset, int len) { + String str = decode(in, offset, offset + len); + if (str.contains("secret_key")) { + containSecretKey = true; + } + } + + @Override + public void endBlob(AnyObjectId id) + throws CorruptObjectException { + if (containSecretKey) { + throw new CorruptObjectException( + "don't add a secret key"); + } + } + }; + } + }; + thrown.expect(CorruptObjectException.class); + checker.check(OBJ_BLOB, encodeASCII("key = \"secret_key\"")); + } + + @Test public void testValidCommitNoParent() throws CorruptObjectException { StringBuilder b = new StringBuilder(); 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 fc520ab17f..3eb049758e 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 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, 2014 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 @@ -43,10 +43,13 @@ package org.eclipse.jgit.transport; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import java.io.File; @@ -58,9 +61,12 @@ import org.eclipse.jgit.junit.RepositoryTestCase; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.transport.OpenSshConfig.Host; import org.eclipse.jgit.util.FileUtils; +import org.eclipse.jgit.util.SystemReader; import org.junit.Before; import org.junit.Test; +import com.jcraft.jsch.ConfigRepository; + public class OpenSshConfigTest extends RepositoryTestCase { private File home; @@ -79,15 +85,18 @@ public class OpenSshConfigTest extends RepositoryTestCase { configFile = new File(new File(home, ".ssh"), Constants.CONFIG); FileUtils.mkdir(configFile.getParentFile()); - System.setProperty("user.name", "jex_junit"); + mockSystemReader.setProperty(Constants.OS_USER_NAME_KEY, "jex_junit"); osc = new OpenSshConfig(home, configFile); } private void config(final String data) throws IOException { - final OutputStreamWriter fw = new OutputStreamWriter( - new FileOutputStream(configFile), "UTF-8"); - fw.write(data); - fw.close(); + long lastMtime = configFile.lastModified(); + do { + try (final OutputStreamWriter fw = new OutputStreamWriter( + new FileOutputStream(configFile), "UTF-8")) { + fw.write(data); + } + } while (lastMtime == configFile.lastModified()); } @Test @@ -155,13 +164,18 @@ public class OpenSshConfigTest extends RepositoryTestCase { @Test public void testAlias_DoesNotMatch() throws Exception { - config("Host orcz\n" + "\tHostName repo.or.cz\n"); + config("Host orcz\n" + "Port 29418\n" + "\tHostName repo.or.cz\n"); final Host h = osc.lookup("repo.or.cz"); assertNotNull(h); assertEquals("repo.or.cz", h.getHostName()); assertEquals("jex_junit", h.getUser()); assertEquals(22, h.getPort()); assertNull(h.getIdentityFile()); + final Host h2 = osc.lookup("orcz"); + assertEquals("repo.or.cz", h.getHostName()); + assertEquals("jex_junit", h.getUser()); + assertEquals(29418, h2.getPort()); + assertNull(h.getIdentityFile()); } @Test @@ -282,4 +296,193 @@ public class OpenSshConfigTest extends RepositoryTestCase { assertNotNull(h); assertEquals(1, h.getConnectionAttempts()); } + + @Test + public void testDefaultBlock() throws Exception { + config("ConnectionAttempts 5\n\nHost orcz\nConnectionAttempts 3\n"); + final Host h = osc.lookup("orcz"); + assertNotNull(h); + assertEquals(5, h.getConnectionAttempts()); + } + + @Test + public void testHostCaseInsensitive() throws Exception { + config("hOsT orcz\nConnectionAttempts 3\n"); + final Host h = osc.lookup("orcz"); + assertNotNull(h); + assertEquals(3, h.getConnectionAttempts()); + } + + @Test + public void testListValueSingle() throws Exception { + config("Host orcz\nUserKnownHostsFile /foo/bar\n"); + final ConfigRepository.Config c = osc.getConfig("orcz"); + assertNotNull(c); + assertEquals("/foo/bar", c.getValue("UserKnownHostsFile")); + } + + @Test + public void testListValueMultiple() throws Exception { + // Tilde expansion doesn't occur within the parser + config("Host orcz\nUserKnownHostsFile \"~/foo/ba z\" /foo/bar \n"); + final ConfigRepository.Config c = osc.getConfig("orcz"); + assertNotNull(c); + assertArrayEquals(new Object[] { "~/foo/ba z", "/foo/bar" }, + c.getValues("UserKnownHostsFile")); + } + + @Test + public void testRepeatedLookups() throws Exception { + config("Host orcz\n" + "\tConnectionAttempts 5\n"); + final Host h1 = osc.lookup("orcz"); + final Host h2 = osc.lookup("orcz"); + assertNotNull(h1); + assertSame(h1, h2); + assertEquals(5, h1.getConnectionAttempts()); + assertEquals(h1.getConnectionAttempts(), h2.getConnectionAttempts()); + final ConfigRepository.Config c = osc.getConfig("orcz"); + assertNotNull(c); + assertSame(c, h1.getConfig()); + assertSame(c, h2.getConfig()); + } + + @Test + public void testRepeatedLookupsWithModification() throws Exception { + config("Host orcz\n" + "\tConnectionAttempts -1\n"); + final Host h1 = osc.lookup("orcz"); + assertNotNull(h1); + assertEquals(1, h1.getConnectionAttempts()); + config("Host orcz\n" + "\tConnectionAttempts 5\n"); + final Host h2 = osc.lookup("orcz"); + assertNotNull(h2); + assertNotSame(h1, h2); + assertEquals(5, h2.getConnectionAttempts()); + assertEquals(1, h1.getConnectionAttempts()); + assertNotSame(h1.getConfig(), h2.getConfig()); + } + + @Test + public void testIdentityFile() throws Exception { + config("Host orcz\nIdentityFile \"~/foo/ba z\"\nIdentityFile /foo/bar"); + final Host h = osc.lookup("orcz"); + assertNotNull(h); + File f = h.getIdentityFile(); + assertNotNull(f); + // Host does tilde replacement + assertEquals(new File(home, "foo/ba z"), f); + final ConfigRepository.Config c = h.getConfig(); + // Config doesn't + assertArrayEquals(new Object[] { "~/foo/ba z", "/foo/bar" }, + c.getValues("IdentityFile")); + } + + @Test + public void testMultiIdentityFile() throws Exception { + config("IdentityFile \"~/foo/ba z\"\nHost orcz\nIdentityFile /foo/bar\nHOST *\nIdentityFile /foo/baz"); + final Host h = osc.lookup("orcz"); + assertNotNull(h); + File f = h.getIdentityFile(); + assertNotNull(f); + // Host does tilde replacement + assertEquals(new File(home, "foo/ba z"), f); + final ConfigRepository.Config c = h.getConfig(); + // Config doesn't + assertArrayEquals(new Object[] { "~/foo/ba z", "/foo/bar", "/foo/baz" }, + c.getValues("IdentityFile")); + } + + @Test + public void testNegatedPattern() throws Exception { + config("Host repo.or.cz\nIdentityFile ~/foo/bar\nHOST !*.or.cz\nIdentityFile /foo/baz"); + final Host h = osc.lookup("repo.or.cz"); + assertNotNull(h); + assertEquals(new File(home, "foo/bar"), h.getIdentityFile()); + assertArrayEquals(new Object[] { "~/foo/bar" }, + h.getConfig().getValues("IdentityFile")); + } + + @Test + public void testPattern() throws Exception { + config("Host repo.or.cz\nIdentityFile ~/foo/bar\nHOST *.or.cz\nIdentityFile /foo/baz"); + final Host h = osc.lookup("repo.or.cz"); + assertNotNull(h); + assertEquals(new File(home, "foo/bar"), h.getIdentityFile()); + assertArrayEquals(new Object[] { "~/foo/bar", "/foo/baz" }, + h.getConfig().getValues("IdentityFile")); + } + + @Test + public void testMultiHost() throws Exception { + config("Host orcz *.or.cz\nIdentityFile ~/foo/bar\nHOST *.or.cz\nIdentityFile /foo/baz"); + final Host h1 = osc.lookup("repo.or.cz"); + assertNotNull(h1); + assertEquals(new File(home, "foo/bar"), h1.getIdentityFile()); + assertArrayEquals(new Object[] { "~/foo/bar", "/foo/baz" }, + h1.getConfig().getValues("IdentityFile")); + final Host h2 = osc.lookup("orcz"); + assertNotNull(h2); + assertEquals(new File(home, "foo/bar"), h2.getIdentityFile()); + assertArrayEquals(new Object[] { "~/foo/bar" }, + h2.getConfig().getValues("IdentityFile")); + } + + @Test + public void testEqualsSign() throws Exception { + config("Host=orcz\n\tConnectionAttempts = 5\n\tUser=\t foobar\t\n"); + final Host h = osc.lookup("orcz"); + assertNotNull(h); + assertEquals(5, h.getConnectionAttempts()); + assertEquals("foobar", h.getUser()); + } + + @Test + public void testMissingArgument() throws Exception { + config("Host=orcz\n\tSendEnv\nIdentityFile\t\nForwardX11\n\tUser=\t foobar\t\n"); + final Host h = osc.lookup("orcz"); + assertNotNull(h); + assertEquals("foobar", h.getUser()); + assertArrayEquals(new String[0], h.getConfig().getValues("SendEnv")); + assertNull(h.getIdentityFile()); + assertNull(h.getConfig().getValue("ForwardX11")); + } + + @Test + public void testHomeDirUserReplacement() throws Exception { + config("Host=orcz\n\tIdentityFile %d/.ssh/%u_id_dsa"); + final Host h = osc.lookup("orcz"); + assertNotNull(h); + assertEquals(new File(new File(home, ".ssh"), "jex_junit_id_dsa"), + h.getIdentityFile()); + } + + @Test + public void testHostnameReplacement() throws Exception { + config("Host=orcz\nHost *.*\n\tHostname %h\nHost *\n\tHostname %h.example.org"); + final Host h = osc.lookup("orcz"); + assertNotNull(h); + assertEquals("orcz.example.org", h.getHostName()); + } + + @Test + public void testRemoteUserReplacement() throws Exception { + config("Host=orcz\n\tUser foo\n" + "Host *.*\n\tHostname %h\n" + + "Host *\n\tHostname %h.ex%%20ample.org\n\tIdentityFile ~/.ssh/%h_%r_id_dsa"); + final Host h = osc.lookup("orcz"); + assertNotNull(h); + assertEquals( + new File(new File(home, ".ssh"), + "orcz.ex%20ample.org_foo_id_dsa"), + h.getIdentityFile()); + } + + @Test + public void testLocalhostFQDNReplacement() throws Exception { + String localhost = SystemReader.getInstance().getHostname(); + config("Host=orcz\n\tIdentityFile ~/.ssh/%l_id_dsa"); + final Host h = osc.lookup("orcz"); + assertNotNull(h); + assertEquals( + new File(new File(home, ".ssh"), localhost + "_id_dsa"), + h.getIdentityFile()); + } } diff --git a/org.eclipse.jgit.ui/.settings/org.eclipse.jdt.ui.prefs b/org.eclipse.jgit.ui/.settings/org.eclipse.jdt.ui.prefs index c336cce6ed..80d38d9c44 100644 --- a/org.eclipse.jgit.ui/.settings/org.eclipse.jdt.ui.prefs +++ b/org.eclipse.jgit.ui/.settings/org.eclipse.jdt.ui.prefs @@ -9,21 +9,23 @@ org.eclipse.jdt.ui.staticondemandthreshold=99 org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8"?><templates/> sp_cleanup.add_default_serial_version_id=true sp_cleanup.add_generated_serial_version_id=false -sp_cleanup.add_missing_annotations=false +sp_cleanup.add_missing_annotations=true sp_cleanup.add_missing_deprecated_annotations=true sp_cleanup.add_missing_methods=false sp_cleanup.add_missing_nls_tags=false sp_cleanup.add_missing_override_annotations=true -sp_cleanup.add_missing_override_annotations_interface_methods=false +sp_cleanup.add_missing_override_annotations_interface_methods=true sp_cleanup.add_serial_version_id=false sp_cleanup.always_use_blocks=true sp_cleanup.always_use_parentheses_in_expressions=false sp_cleanup.always_use_this_for_non_static_field_access=false sp_cleanup.always_use_this_for_non_static_method_access=false +sp_cleanup.convert_functional_interfaces=false sp_cleanup.convert_to_enhanced_for_loop=false sp_cleanup.correct_indentation=false sp_cleanup.format_source_code=true sp_cleanup.format_source_code_changes_only=true +sp_cleanup.insert_inferred_type_arguments=false sp_cleanup.make_local_variable_final=false sp_cleanup.make_parameters_final=false sp_cleanup.make_private_fields_final=true @@ -32,19 +34,20 @@ sp_cleanup.make_variable_declarations_final=false sp_cleanup.never_use_blocks=false sp_cleanup.never_use_parentheses_in_expressions=true sp_cleanup.on_save_use_additional_actions=true -sp_cleanup.organize_imports=false +sp_cleanup.organize_imports=true sp_cleanup.qualify_static_field_accesses_with_declaring_class=false sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true sp_cleanup.qualify_static_member_accesses_with_declaring_class=false sp_cleanup.qualify_static_method_accesses_with_declaring_class=false sp_cleanup.remove_private_constructors=true +sp_cleanup.remove_redundant_type_arguments=true sp_cleanup.remove_trailing_whitespaces=true sp_cleanup.remove_trailing_whitespaces_all=true sp_cleanup.remove_trailing_whitespaces_ignore_empty=false -sp_cleanup.remove_unnecessary_casts=false -sp_cleanup.remove_unnecessary_nls_tags=false -sp_cleanup.remove_unused_imports=false +sp_cleanup.remove_unnecessary_casts=true +sp_cleanup.remove_unnecessary_nls_tags=true +sp_cleanup.remove_unused_imports=true sp_cleanup.remove_unused_local_variables=false sp_cleanup.remove_unused_private_fields=true sp_cleanup.remove_unused_private_members=false @@ -52,8 +55,10 @@ sp_cleanup.remove_unused_private_methods=true sp_cleanup.remove_unused_private_types=true sp_cleanup.sort_members=false sp_cleanup.sort_members_all=false -sp_cleanup.use_blocks=false +sp_cleanup.use_anonymous_class_creation=false +sp_cleanup.use_blocks=true sp_cleanup.use_blocks_only_for_return_and_throw=false +sp_cleanup.use_lambda=false sp_cleanup.use_parentheses_in_expressions=false sp_cleanup.use_this_for_non_static_field_access=false sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true diff --git a/org.eclipse.jgit/.settings/org.eclipse.jdt.ui.prefs b/org.eclipse.jgit/.settings/org.eclipse.jdt.ui.prefs index c336cce6ed..80d38d9c44 100644 --- a/org.eclipse.jgit/.settings/org.eclipse.jdt.ui.prefs +++ b/org.eclipse.jgit/.settings/org.eclipse.jdt.ui.prefs @@ -9,21 +9,23 @@ org.eclipse.jdt.ui.staticondemandthreshold=99 org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8"?><templates/> sp_cleanup.add_default_serial_version_id=true sp_cleanup.add_generated_serial_version_id=false -sp_cleanup.add_missing_annotations=false +sp_cleanup.add_missing_annotations=true sp_cleanup.add_missing_deprecated_annotations=true sp_cleanup.add_missing_methods=false sp_cleanup.add_missing_nls_tags=false sp_cleanup.add_missing_override_annotations=true -sp_cleanup.add_missing_override_annotations_interface_methods=false +sp_cleanup.add_missing_override_annotations_interface_methods=true sp_cleanup.add_serial_version_id=false sp_cleanup.always_use_blocks=true sp_cleanup.always_use_parentheses_in_expressions=false sp_cleanup.always_use_this_for_non_static_field_access=false sp_cleanup.always_use_this_for_non_static_method_access=false +sp_cleanup.convert_functional_interfaces=false sp_cleanup.convert_to_enhanced_for_loop=false sp_cleanup.correct_indentation=false sp_cleanup.format_source_code=true sp_cleanup.format_source_code_changes_only=true +sp_cleanup.insert_inferred_type_arguments=false sp_cleanup.make_local_variable_final=false sp_cleanup.make_parameters_final=false sp_cleanup.make_private_fields_final=true @@ -32,19 +34,20 @@ sp_cleanup.make_variable_declarations_final=false sp_cleanup.never_use_blocks=false sp_cleanup.never_use_parentheses_in_expressions=true sp_cleanup.on_save_use_additional_actions=true -sp_cleanup.organize_imports=false +sp_cleanup.organize_imports=true sp_cleanup.qualify_static_field_accesses_with_declaring_class=false sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true sp_cleanup.qualify_static_member_accesses_with_declaring_class=false sp_cleanup.qualify_static_method_accesses_with_declaring_class=false sp_cleanup.remove_private_constructors=true +sp_cleanup.remove_redundant_type_arguments=true sp_cleanup.remove_trailing_whitespaces=true sp_cleanup.remove_trailing_whitespaces_all=true sp_cleanup.remove_trailing_whitespaces_ignore_empty=false -sp_cleanup.remove_unnecessary_casts=false -sp_cleanup.remove_unnecessary_nls_tags=false -sp_cleanup.remove_unused_imports=false +sp_cleanup.remove_unnecessary_casts=true +sp_cleanup.remove_unnecessary_nls_tags=true +sp_cleanup.remove_unused_imports=true sp_cleanup.remove_unused_local_variables=false sp_cleanup.remove_unused_private_fields=true sp_cleanup.remove_unused_private_members=false @@ -52,8 +55,10 @@ sp_cleanup.remove_unused_private_methods=true sp_cleanup.remove_unused_private_types=true sp_cleanup.sort_members=false sp_cleanup.sort_members_all=false -sp_cleanup.use_blocks=false +sp_cleanup.use_anonymous_class_creation=false +sp_cleanup.use_blocks=true sp_cleanup.use_blocks_only_for_return_and_throw=false +sp_cleanup.use_lambda=false sp_cleanup.use_parentheses_in_expressions=false sp_cleanup.use_this_for_non_static_field_access=false sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true 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 bf2a4a2e0f..8d3931493c 100644 --- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties +++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties @@ -365,7 +365,7 @@ invalidPathContainsSeparator=Invalid path (contains separator ''{0}''): {1} invalidPathPeriodAtEndWindows=Invalid path (period at end is ignored by Windows): {0} invalidPathSpaceAtEndWindows=Invalid path (space at end is ignored by Windows): {0} invalidPathReservedOnWindows=Invalid path (''{0}'' is reserved on Windows): {1} -invalidRedirectLocation=Redirect or URI ''{0}'': invalid redirect location {1} -> {2} +invalidRedirectLocation=Invalid redirect location {0} -> {1} invalidReflogRevision=Invalid reflog revision: {0} invalidRefName=Invalid ref name: {0} invalidReftableBlock=Invalid reftable block @@ -535,11 +535,11 @@ receivePackObjectTooLarge2=Object too large ({0} bytes), rejecting the pack. Max receivePackInvalidLimit=Illegal limit parameter value {0} receivePackTooLarge=Pack exceeds the limit of {0} bytes, rejecting the pack receivingObjects=Receiving objects -redirectBlocked=URI ''{0}'' redirection blocked: redirect {1} -> {2} not allowed +redirectBlocked=Redirection blocked: redirect {0} -> {1} not allowed redirectHttp=URI ''{0}'': following HTTP redirect #{1} {2} -> {3} -redirectLimitExceeded=URI ''{0}'' redirected more than {1} times; aborted at {2} -> {3} -redirectLocationMissing=Invalid redirect of ''{0}'': no redirect location for {1} -redirectsOff=Cannot redirect ''{0}'': http.followRedirects is false (HTTP status {1}) +redirectLimitExceeded=Redirected more than {0} times; aborted at {1} -> {2} +redirectLocationMissing=Invalid redirect: no redirect location for {0} +redirectsOff=Cannot redirect because http.followRedirects is false (HTTP status {0}) refAlreadyExists=already exists refAlreadyExists1=Ref {0} already exists reflogEntryNotFound=Entry {0} not found in reflog for ''{1}'' @@ -587,7 +587,7 @@ secondsAgo={0} seconds ago selectingCommits=Selecting commits sequenceTooLargeForDiffAlgorithm=Sequence too large for difference algorithm. serviceNotEnabledNoName=Service not enabled -serviceNotPermitted={0} not permitted +serviceNotPermitted={1} not permitted on ''{0}'' sha1CollisionDetected1=SHA-1 collision detected on {0} shallowCommitsAlreadyInitialized=Shallow commits have already been initialized shallowPacksRequireDepthWalk=Shallow packs require a DepthWalk 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 d450c64679..bde8e63d1d 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java @@ -50,6 +50,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; +import org.eclipse.jgit.annotations.Nullable; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.api.errors.InvalidRemoteException; import org.eclipse.jgit.api.errors.JGitInternalException; @@ -157,6 +158,16 @@ public class CloneCommand extends TransportCommand<CloneCommand, Git> { } /** + * Get the git directory. This is primarily used for tests. + * + * @return the git directory + */ + @Nullable + File getDirectory() { + return directory; + } + + /** * Executes the {@code Clone} command. * * The Git instance returned by this command needs to be closed by the @@ -232,9 +243,9 @@ public class CloneCommand extends TransportCommand<CloneCommand, Git> { return false; } - private void verifyDirectories(URIish u) { + void verifyDirectories(URIish u) { if (directory == null && gitDir == null) { - directory = new File(u.getHumanishName(), Constants.DOT_GIT); + directory = new File(u.getHumanishName() + (bare ? Constants.DOT_GIT_EXT : "")); //$NON-NLS-1$ } directoryExistsInitially = directory != null && directory.exists(); gitDirExistsInitially = gitDir != null && gitDir.exists(); 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 d365171ed1..68b1bd9e29 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/DescribeCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/DescribeCommand.java @@ -209,7 +209,8 @@ public class DescribeCommand extends GitCommand<String> { // Find the first tag that matches one of the matchers; precedence according to matcher definition order for (IMatcher matcher : matchers) { Optional<Ref> match = tags.stream() - .filter(tag -> matcher.matches(tag.getName(), false)) + .filter(tag -> matcher.matches(tag.getName(), false, + false)) .findFirst(); if (match.isPresent()) { return match; 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 485315968a..8f83de79ac 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java @@ -192,6 +192,7 @@ public class FetchCommand extends TransportCommand<FetchCommand, FetchResult> { .setThin(thin).setRefSpecs(refSpecs) .setDryRun(dryRun) .setRecurseSubmodules(recurseMode); + configure(f); if (callback != null) { callback.fetchingSubmodule(walk.getPath()); } 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 b88a16e86c..3cf5de8be5 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesRule.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesRule.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010, Red Hat Inc. + * Copyright (C) 2010, 2017 Red Hat Inc. * and other copyright owners as documented in the project's IP log. * * This program and the accompanying materials are made available @@ -210,7 +210,7 @@ public class AttributesRule { return false; if (relativeTarget.length() == 0) return false; - boolean match = matcher.matches(relativeTarget, isDirectory); + boolean match = matcher.matches(relativeTarget, isDirectory, true); return match; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/FastIgnoreRule.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/FastIgnoreRule.java index ef67d49419..7298a082c7 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/FastIgnoreRule.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/FastIgnoreRule.java @@ -155,7 +155,7 @@ public class FastIgnoreRule { return false; if (path.length() == 0) return false; - boolean match = matcher.matches(path, directory); + boolean match = matcher.matches(path, directory, false); return match; } 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 61f7b83400..5b184cb19f 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 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014, Andrey Loskutov <loskutov@gmx.de> + * Copyright (C) 2014, 2017 Andrey Loskutov <loskutov@gmx.de> * and other copyright owners as documented in the project's IP log. * * This program and the accompanying materials are made available @@ -52,7 +52,8 @@ public interface IMatcher { */ public static final IMatcher NO_MATCH = new IMatcher() { @Override - public boolean matches(String path, boolean assumeDirectory) { + public boolean matches(String path, boolean assumeDirectory, + boolean pathMatch) { return false; } @@ -71,9 +72,14 @@ public interface IMatcher { * @param assumeDirectory * true to assume this path as directory (even if it doesn't end * with a slash) + * @param pathMatch + * {@code true} if the match is for the full path: prefix-only + * matches are not allowed, and {@link NameMatcher}s must match + * only the last component (if they can -- they may not, if they + * are anchored at the beginning) * @return true if this matcher pattern matches given string */ - boolean matches(String path, boolean assumeDirectory); + boolean matches(String path, boolean assumeDirectory, boolean pathMatch); /** * Matches only part of given string 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 00651237db..9667837a41 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 @@ -64,26 +64,59 @@ public class NameMatcher extends AbstractMatcher { pattern = Strings.deleteBackslash(pattern); } beginning = pattern.length() == 0 ? false : pattern.charAt(0) == slash; - if (!beginning) + if (!beginning) { this.subPattern = pattern; - else + } else { this.subPattern = pattern.substring(1); + } } @Override - public boolean matches(String path, boolean assumeDirectory) { - int end = 0; - int firstChar = 0; - do { - firstChar = getFirstNotSlash(path, end); - end = getFirstSlash(path, firstChar); - boolean match = matches(path, firstChar, end, assumeDirectory); - if (match) + public boolean matches(String path, boolean assumeDirectory, + boolean pathMatch) { + // A NameMatcher's pattern does not contain a slash. + int start = 0; + int stop = path.length(); + if (stop > 0 && path.charAt(0) == slash) { + start++; + } + if (pathMatch) { + // Can match only after the last slash + int lastSlash = path.lastIndexOf(slash, stop - 1); + if (lastSlash == stop - 1) { + // Skip trailing slash + lastSlash = path.lastIndexOf(slash, lastSlash - 1); + stop--; + } + boolean match; + if (lastSlash < start) { + match = matches(path, start, stop, assumeDirectory); + } else { + // Can't match if the path contains a slash if the pattern is + // anchored at the beginning + match = !beginning + && matches(path, lastSlash + 1, stop, assumeDirectory); + } + if (match && dirOnly) { + match = assumeDirectory; + } + return match; + } + while (start < stop) { + int end = path.indexOf(slash, start); + if (end < 0) { + end = stop; + } + if (end > start && matches(path, start, end, assumeDirectory)) { // make sure the directory matches: either if we are done with // segment and there is next one, or if the directory is assumed - return !dirOnly ? true : (end > 0 && end != path.length()) - || assumeDirectory; - } while (!beginning && end != path.length()); + return !dirOnly || assumeDirectory || end < stop; + } + if (beginning) { + break; + } + start = end + 1; + } return false; } @@ -92,25 +125,18 @@ public class NameMatcher extends AbstractMatcher { boolean assumeDirectory) { // faster local access, same as in string.indexOf() String s = subPattern; - if (s.length() != (endExcl - startIncl)) + int length = s.length(); + if (length != (endExcl - startIncl)) { return false; - for (int i = 0; i < s.length(); i++) { + } + for (int i = 0; i < length; i++) { char c1 = s.charAt(i); char c2 = segment.charAt(i + startIncl); - if (c1 != c2) + if (c1 != c2) { return false; + } } return true; } - private int getFirstNotSlash(String s, int start) { - int slashIdx = s.indexOf(slash, start); - return slashIdx == start ? start + 1 : start; - } - - private int getFirstSlash(String s, int start) { - int slashIdx = s.indexOf(slash, start); - return slashIdx == -1 ? s.length() : slashIdx; - } - } 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 ce9ad80fb0..9b3a2aac78 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 @@ -52,7 +52,6 @@ import java.util.ArrayList; import java.util.List; import org.eclipse.jgit.errors.InvalidPatternException; -import org.eclipse.jgit.ignore.FastIgnoreRule; import org.eclipse.jgit.ignore.internal.Strings.PatternState; /** @@ -68,9 +67,10 @@ public class PathMatcher extends AbstractMatcher { private final char slash; - private boolean beginning; + private final boolean beginning; - PathMatcher(String pattern, Character pathSeparator, boolean dirOnly) + private PathMatcher(String pattern, Character pathSeparator, + boolean dirOnly) throws InvalidPatternException { super(pattern, dirOnly); slash = getPathSeparator(pathSeparator); @@ -87,7 +87,7 @@ public class PathMatcher extends AbstractMatcher { && count(path, slash, true) > 0; } - static private List<IMatcher> createMatchers(List<String> segments, + private static List<IMatcher> createMatchers(List<String> segments, Character pathSeparator, boolean dirOnly) throws InvalidPatternException { List<IMatcher> matchers = new ArrayList<>(segments.size()); @@ -171,10 +171,12 @@ public class PathMatcher extends AbstractMatcher { } @Override - public boolean matches(String path, boolean assumeDirectory) { - if (matchers == null) - return simpleMatch(path, assumeDirectory); - return iterate(path, 0, path.length(), assumeDirectory); + public boolean matches(String path, boolean assumeDirectory, + boolean pathMatch) { + if (matchers == null) { + return simpleMatch(path, assumeDirectory, pathMatch); + } + return iterate(path, 0, path.length(), assumeDirectory, pathMatch); } /* @@ -182,31 +184,31 @@ public class PathMatcher extends AbstractMatcher { * wildcards or single segments (mean: this is multi-segment path which must * be at the beginning of the another string) */ - private boolean simpleMatch(String path, boolean assumeDirectory) { + private boolean simpleMatch(String path, boolean assumeDirectory, + boolean pathMatch) { boolean hasSlash = path.indexOf(slash) == 0; - if (beginning && !hasSlash) + if (beginning && !hasSlash) { path = slash + path; - - if (!beginning && hasSlash) + } + if (!beginning && hasSlash) { path = path.substring(1); - - if (path.equals(pattern)) - // Exact match - if (dirOnly && !assumeDirectory) - // Directory expectations not met - return false; - else - // Directory expectations met - return true; - + } + if (path.equals(pattern)) { + // Exact match: must meet directory expectations + return !dirOnly || assumeDirectory; + } /* * Add slashes for startsWith check. This avoids matching e.g. * "/src/new" to /src/newfile" but allows "/src/new" to match * "/src/new/newfile", as is the git standard */ - if (path.startsWith(pattern + FastIgnoreRule.PATH_SEPARATOR)) + String prefix = pattern + slash; + if (pathMatch) { + return path.equals(prefix) && (!dirOnly || assumeDirectory); + } + if (path.startsWith(prefix)) { return true; - + } return false; } @@ -217,12 +219,17 @@ public class PathMatcher extends AbstractMatcher { "Path matcher works only on entire paths"); //$NON-NLS-1$ } - boolean iterate(final String path, final int startIncl, final int endExcl, - boolean assumeDirectory) { + private boolean iterate(final String path, final int startIncl, + final int endExcl, boolean assumeDirectory, boolean pathMatch) { int matcher = 0; int right = startIncl; boolean match = false; int lastWildmatch = -1; + // ** matches may get extended if a later match fails. When that + // happens, we must extend the ** by exactly one segment. + // wildmatchBacktrackPos records the end of the segment after a ** + // match, so that we can reset correctly. + int wildmatchBacktrackPos = -1; while (true) { int left = right; right = path.indexOf(slash, right); @@ -248,6 +255,9 @@ public class PathMatcher extends AbstractMatcher { } return match && matcher + 1 == matchers.size(); } + if (wildmatchBacktrackPos < 0) { + wildmatchBacktrackPos = right; + } if (right - left > 0) { match = matches(matcher, path, left, right, assumeDirectory); } else { @@ -256,17 +266,44 @@ public class PathMatcher extends AbstractMatcher { continue; } if (match) { - if (matchers.get(matcher) == WILD) { + boolean wasWild = matchers.get(matcher) == WILD; + if (wasWild) { lastWildmatch = matcher; + wildmatchBacktrackPos = -1; // ** can match *nothing*: a/**/b match also a/b right = left - 1; } matcher++; if (matcher == matchers.size()) { - return true; + // We had a prefix match here. + if (!pathMatch) { + return true; + } else { + if (right == endExcl - 1) { + // Extra slash at the end: actually a full match. + // Must meet directory expectations + return !dirOnly || assumeDirectory; + } + // Prefix matches only if pattern ended with /** + if (wasWild) { + return true; + } + if (lastWildmatch >= 0) { + // Consider pattern **/x and input x/x. + // We've matched the prefix x/ so far: we + // must try to extend the **! + matcher = lastWildmatch + 1; + right = wildmatchBacktrackPos; + wildmatchBacktrackPos = -1; + } else { + return false; + } + } } } else if (lastWildmatch != -1) { matcher = lastWildmatch + 1; + right = wildmatchBacktrackPos; + wildmatchBacktrackPos = -1; } else { return false; } @@ -274,7 +311,8 @@ public class PathMatcher extends AbstractMatcher { } } - boolean matches(int matcherIdx, String path, int startIncl, int endExcl, + private boolean matches(int matcherIdx, String path, int startIncl, + int endExcl, boolean assumeDirectory) { IMatcher matcher = matchers.get(matcherIdx); return matcher.matches(path, startIncl, endExcl, assumeDirectory); 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 93ea13c726..363b3cee84 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 @@ -62,7 +62,8 @@ public final class WildMatcher extends AbstractMatcher { } @Override - public final boolean matches(String path, boolean assumeDirectory) { + public final boolean matches(String path, boolean assumeDirectory, + boolean pathMatch) { return true; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/ObjectToPack.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/ObjectToPack.java index a0896577f3..bc7a603c4a 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/ObjectToPack.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/ObjectToPack.java @@ -182,6 +182,7 @@ public class ObjectToPack extends PackedObjectInfo { } /** @return the type of this object. */ + @Override public final int getType() { return (flags >> TYPE_SHIFT) & 0x7; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BlobObjectChecker.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BlobObjectChecker.java new file mode 100644 index 0000000000..0fe63ae5b4 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BlobObjectChecker.java @@ -0,0 +1,92 @@ +/* + * 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.lib; + +import org.eclipse.jgit.errors.CorruptObjectException; + +/** + * Verifies that a blob object is a valid object. + * <p> + * Unlike trees, commits and tags, there's no validity of blobs. Implementers + * can optionally implement this blob checker to reject certain blobs. + * + * @since 4.9 + */ +public interface BlobObjectChecker { + /** No-op implementation of {@link BlobObjectChecker}. */ + public static final BlobObjectChecker NULL_CHECKER = + new BlobObjectChecker() { + @Override + public void update(byte[] in, int p, int len) { + // Empty implementation. + } + + @Override + public void endBlob(AnyObjectId id) { + // Empty implementation. + } + }; + + /** + * Check a new fragment of the blob. + * + * @param in + * input array of bytes. + * @param offset + * offset to start at from {@code in}. + * @param len + * length of the fragment to check. + */ + void update(byte[] in, int offset, int len); + + /** + * Finalize the blob checking. + * + * @param id + * identity of the object being checked. + * @throws CorruptObjectException + * if any error was detected. + */ + void endBlob(AnyObjectId id) throws CorruptObjectException; +} 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 f45c71cf95..6281bcfb3d 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java @@ -62,8 +62,6 @@ import java.util.Locale; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import org.eclipse.jgit.errors.ConfigInvalidException; import org.eclipse.jgit.events.ConfigChangedEvent; @@ -71,21 +69,26 @@ import org.eclipse.jgit.events.ConfigChangedListener; import org.eclipse.jgit.events.ListenerHandle; import org.eclipse.jgit.events.ListenerList; import org.eclipse.jgit.internal.JGitText; +import org.eclipse.jgit.transport.RefSpec; import org.eclipse.jgit.util.IO; import org.eclipse.jgit.util.RawParseUtils; -import org.eclipse.jgit.util.StringUtils; - /** * Git style {@code .config}, {@code .gitconfig}, {@code .gitmodules} file. */ public class Config { + private static final String[] EMPTY_STRING_ARRAY = {}; - private static final long KiB = 1024; - private static final long MiB = 1024 * KiB; - private static final long GiB = 1024 * MiB; + + static final long KiB = 1024; + static final long MiB = 1024 * KiB; + static final long GiB = 1024 * MiB; private static final int MAX_DEPTH = 10; + private static final TypedConfigGetter DEFAULT_GETTER = new DefaultTypedConfigGetter(); + + private static TypedConfigGetter typedGetter = DEFAULT_GETTER; + /** the change listeners */ private final ListenerList listeners = new ListenerList(); @@ -106,7 +109,7 @@ public class Config { * must ensure it is a special copy of the empty string. It also must * be treated like the empty string. */ - private static final String MAGIC_EMPTY_VALUE = new String(); + static final String MAGIC_EMPTY_VALUE = new String(); /** Create a configuration with no default fallback. */ public Config() { @@ -126,6 +129,18 @@ public class Config { } /** + * Globally sets a {@link TypedConfigGetter} that is subsequently used to + * read typed values from all git configs. + * + * @param getter + * to use; if {@code null} use the default getter. + * @since 4.9 + */ + public static void setTypedConfigGetter(TypedConfigGetter getter) { + typedGetter = getter == null ? DEFAULT_GETTER : getter; + } + + /** * Escape the value before saving * * @param x @@ -206,7 +221,7 @@ public class Config { */ public int getInt(final String section, final String name, final int defaultValue) { - return getInt(section, null, name, defaultValue); + return typedGetter.getInt(this, section, null, name, defaultValue); } /** @@ -224,11 +239,8 @@ public class Config { */ public int getInt(final String section, String subsection, final String name, final int defaultValue) { - final long val = getLong(section, subsection, name, defaultValue); - if (Integer.MIN_VALUE <= val && val <= Integer.MAX_VALUE) - return (int) val; - throw new IllegalArgumentException(MessageFormat.format(JGitText.get().integerValueOutOfRange - , section, name)); + return typedGetter.getInt(this, section, subsection, name, + defaultValue); } /** @@ -243,7 +255,7 @@ public class Config { * @return an integer value from the configuration, or defaultValue. */ public long getLong(String section, String name, long defaultValue) { - return getLong(section, null, name, defaultValue); + return typedGetter.getLong(this, section, null, name, defaultValue); } /** @@ -261,37 +273,8 @@ public class Config { */ public long getLong(final String section, String subsection, final String name, final long defaultValue) { - final String str = getString(section, subsection, name); - if (str == null) - return defaultValue; - - String n = str.trim(); - if (n.length() == 0) - return defaultValue; - - long mul = 1; - switch (StringUtils.toLowerCase(n.charAt(n.length() - 1))) { - case 'g': - mul = GiB; - break; - case 'm': - mul = MiB; - break; - case 'k': - mul = KiB; - break; - } - if (mul > 1) - n = n.substring(0, n.length() - 1).trim(); - if (n.length() == 0) - return defaultValue; - - try { - return mul * Long.parseLong(n); - } catch (NumberFormatException nfe) { - throw new IllegalArgumentException(MessageFormat.format(JGitText.get().invalidIntegerValue - , section, name, str)); - } + return typedGetter.getLong(this, section, subsection, name, + defaultValue); } /** @@ -308,7 +291,7 @@ public class Config { */ public boolean getBoolean(final String section, final String name, final boolean defaultValue) { - return getBoolean(section, null, name, defaultValue); + return typedGetter.getBoolean(this, section, null, name, defaultValue); } /** @@ -327,17 +310,8 @@ public class Config { */ public boolean getBoolean(final String section, String subsection, final String name, final boolean defaultValue) { - String n = getRawString(section, subsection, name); - if (n == null) - return defaultValue; - if (MAGIC_EMPTY_VALUE == n) - return true; - try { - return StringUtils.toBoolean(n); - } catch (IllegalArgumentException err) { - throw new IllegalArgumentException(MessageFormat.format(JGitText.get().invalidBooleanValue - , section, name, n)); - } + return typedGetter.getBoolean(this, section, subsection, name, + defaultValue); } /** @@ -358,7 +332,8 @@ public class Config { public <T extends Enum<?>> T getEnum(final String section, final String subsection, final String name, final T defaultValue) { final T[] all = allValuesOf(defaultValue); - return getEnum(all, section, subsection, name, defaultValue); + return typedGetter.getEnum(this, all, section, subsection, name, + defaultValue); } @SuppressWarnings("unchecked") @@ -393,55 +368,8 @@ public class Config { */ public <T extends Enum<?>> T getEnum(final T[] all, final String section, final String subsection, final String name, final T defaultValue) { - String value = getString(section, subsection, name); - if (value == null) - return defaultValue; - - if (all[0] instanceof ConfigEnum) { - for (T t : all) { - if (((ConfigEnum) t).matchConfigValue(value)) - return t; - } - } - - String n = value.replace(' ', '_'); - - // Because of c98abc9c0586c73ef7df4172644b7dd21c979e9d being used in - // the real world before its breakage was fully understood, we must - // also accept '-' as though it were ' '. - n = n.replace('-', '_'); - - T trueState = null; - T falseState = null; - for (T e : all) { - if (StringUtils.equalsIgnoreCase(e.name(), n)) - return e; - else if (StringUtils.equalsIgnoreCase(e.name(), "TRUE")) //$NON-NLS-1$ - trueState = e; - else if (StringUtils.equalsIgnoreCase(e.name(), "FALSE")) //$NON-NLS-1$ - falseState = e; - } - - // This is an odd little fallback. C Git sometimes allows boolean - // values in a tri-state with other things. If we have both a true - // and a false value in our enumeration, assume its one of those. - // - if (trueState != null && falseState != null) { - try { - return StringUtils.toBoolean(n) ? trueState : falseState; - } catch (IllegalArgumentException err) { - // Fall through and use our custom error below. - } - } - - if (subsection != null) - throw new IllegalArgumentException(MessageFormat.format( - JGitText.get().enumValueNotSupported3, section, subsection, - name, value)); - else - throw new IllegalArgumentException( - MessageFormat.format(JGitText.get().enumValueNotSupported2, - section, name, value)); + return typedGetter.getEnum(this, all, section, subsection, name, + defaultValue); } /** @@ -515,100 +443,25 @@ public class Config { */ public long getTimeUnit(String section, String subsection, String name, long defaultValue, TimeUnit wantUnit) { - String valueString = getString(section, subsection, name); - - if (valueString == null) { - return defaultValue; - } - - String s = valueString.trim(); - if (s.length() == 0) { - return defaultValue; - } - - if (s.startsWith("-")/* negative */) { //$NON-NLS-1$ - throw notTimeUnit(section, subsection, name, valueString); - } - - Matcher m = Pattern.compile("^(0|[1-9][0-9]*)\\s*(.*)$") //$NON-NLS-1$ - .matcher(valueString); - if (!m.matches()) { - return defaultValue; - } - - String digits = m.group(1); - String unitName = m.group(2).trim(); - - TimeUnit inputUnit; - int inputMul; - - if (unitName.isEmpty()) { - inputUnit = wantUnit; - inputMul = 1; - - } else if (match(unitName, "ms", "milliseconds")) { //$NON-NLS-1$ //$NON-NLS-2$ - inputUnit = TimeUnit.MILLISECONDS; - inputMul = 1; - - } else if (match(unitName, "s", "sec", "second", "seconds")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ - inputUnit = TimeUnit.SECONDS; - inputMul = 1; - - } else if (match(unitName, "m", "min", "minute", "minutes")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ - inputUnit = TimeUnit.MINUTES; - inputMul = 1; - - } else if (match(unitName, "h", "hr", "hour", "hours")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ - inputUnit = TimeUnit.HOURS; - inputMul = 1; - - } else if (match(unitName, "d", "day", "days")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - inputUnit = TimeUnit.DAYS; - inputMul = 1; - - } else if (match(unitName, "w", "week", "weeks")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - inputUnit = TimeUnit.DAYS; - inputMul = 7; - - } else if (match(unitName, "mon", "month", "months")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - inputUnit = TimeUnit.DAYS; - inputMul = 30; - - } else if (match(unitName, "y", "year", "years")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - inputUnit = TimeUnit.DAYS; - inputMul = 365; - - } else { - throw notTimeUnit(section, subsection, name, valueString); - } - - try { - return wantUnit.convert(Long.parseLong(digits) * inputMul, - inputUnit); - } catch (NumberFormatException nfe) { - throw notTimeUnit(section, subsection, unitName, valueString); - } - } - - private static boolean match(final String a, final String... cases) { - for (final String b : cases) { - if (b != null && b.equalsIgnoreCase(a)) { - return true; - } - } - return false; + return typedGetter.getTimeUnit(this, section, subsection, name, + defaultValue, wantUnit); } - private IllegalArgumentException notTimeUnit(String section, - String subsection, String name, String valueString) { - if (subsection != null) { - return new IllegalArgumentException( - MessageFormat.format(JGitText.get().invalidTimeUnitValue3, - section, subsection, name, valueString)); - } - return new IllegalArgumentException( - MessageFormat.format(JGitText.get().invalidTimeUnitValue2, - section, name, valueString)); + /** + * Parse a list of {@link RefSpec}s from the configuration. + * + * @param section + * section the key is in. + * @param subsection + * subsection the key is in, or null if not in a subsection. + * @param name + * the key name. + * @return a possibly empty list of {@link RefSpec}s + * @since 4.9 + */ + public List<RefSpec> getRefSpecs(String section, String subsection, + String name) { + return typedGetter.getRefSpecs(this, section, subsection, name); } /** @@ -757,7 +610,7 @@ public class Config { listeners.dispatch(new ConfigChangedEvent()); } - private String getRawString(final String section, final String subsection, + String getRawString(final String section, final String subsection, final String name) { String[] lst = getRawStringList(section, subsection, name); if (lst != null) { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/DefaultTypedConfigGetter.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/DefaultTypedConfigGetter.java new file mode 100644 index 0000000000..fd37747601 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/DefaultTypedConfigGetter.java @@ -0,0 +1,299 @@ +/* + * Copyright (C) 2017, Thomas Wolf <thomas.wolf@paranor.ch> + * 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; + +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.jgit.annotations.NonNull; +import org.eclipse.jgit.internal.JGitText; +import org.eclipse.jgit.lib.Config.ConfigEnum; +import org.eclipse.jgit.transport.RefSpec; +import org.eclipse.jgit.util.StringUtils; + +/** + * An {@link TypedConfigGetter} that throws {@link IllegalArgumentException} on + * invalid values. + * + * @since 4.9 + */ +public class DefaultTypedConfigGetter implements TypedConfigGetter { + + @Override + public boolean getBoolean(Config config, String section, String subsection, + String name, boolean defaultValue) { + String n = config.getRawString(section, subsection, name); + if (n == null) { + return defaultValue; + } + if (Config.MAGIC_EMPTY_VALUE == n) { + return true; + } + try { + return StringUtils.toBoolean(n); + } catch (IllegalArgumentException err) { + throw new IllegalArgumentException(MessageFormat.format( + JGitText.get().invalidBooleanValue, section, name, n)); + } + } + + @Override + public <T extends Enum<?>> T getEnum(Config config, T[] all, String section, + String subsection, String name, T defaultValue) { + String value = config.getString(section, subsection, name); + if (value == null) { + return defaultValue; + } + if (all[0] instanceof ConfigEnum) { + for (T t : all) { + if (((ConfigEnum) t).matchConfigValue(value)) { + return t; + } + } + } + + String n = value.replace(' ', '_'); + + // Because of c98abc9c0586c73ef7df4172644b7dd21c979e9d being used in + // the real world before its breakage was fully understood, we must + // also accept '-' as though it were ' '. + n = n.replace('-', '_'); + + T trueState = null; + T falseState = null; + for (T e : all) { + if (StringUtils.equalsIgnoreCase(e.name(), n)) { + return e; + } else if (StringUtils.equalsIgnoreCase(e.name(), "TRUE")) { //$NON-NLS-1$ + trueState = e; + } else if (StringUtils.equalsIgnoreCase(e.name(), "FALSE")) { //$NON-NLS-1$ + falseState = e; + } + } + + // This is an odd little fallback. C Git sometimes allows boolean + // values in a tri-state with other things. If we have both a true + // and a false value in our enumeration, assume its one of those. + // + if (trueState != null && falseState != null) { + try { + return StringUtils.toBoolean(n) ? trueState : falseState; + } catch (IllegalArgumentException err) { + // Fall through and use our custom error below. + } + } + + if (subsection != null) { + throw new IllegalArgumentException( + MessageFormat.format(JGitText.get().enumValueNotSupported3, + section, subsection, name, value)); + } else { + throw new IllegalArgumentException( + MessageFormat.format(JGitText.get().enumValueNotSupported2, + section, name, value)); + } + } + + @Override + public int getInt(Config config, String section, String subsection, + String name, int defaultValue) { + long val = config.getLong(section, subsection, name, defaultValue); + if (Integer.MIN_VALUE <= val && val <= Integer.MAX_VALUE) { + return (int) val; + } + throw new IllegalArgumentException(MessageFormat + .format(JGitText.get().integerValueOutOfRange, section, name)); + } + + @Override + public long getLong(Config config, String section, String subsection, + String name, long defaultValue) { + final String str = config.getString(section, subsection, name); + if (str == null) { + return defaultValue; + } + String n = str.trim(); + if (n.length() == 0) { + return defaultValue; + } + long mul = 1; + switch (StringUtils.toLowerCase(n.charAt(n.length() - 1))) { + case 'g': + mul = Config.GiB; + break; + case 'm': + mul = Config.MiB; + break; + case 'k': + mul = Config.KiB; + break; + } + if (mul > 1) { + n = n.substring(0, n.length() - 1).trim(); + } + if (n.length() == 0) { + return defaultValue; + } + try { + return mul * Long.parseLong(n); + } catch (NumberFormatException nfe) { + throw new IllegalArgumentException(MessageFormat.format( + JGitText.get().invalidIntegerValue, section, name, str)); + } + } + + @Override + public long getTimeUnit(Config config, String section, String subsection, + String name, long defaultValue, TimeUnit wantUnit) { + String valueString = config.getString(section, subsection, name); + + if (valueString == null) { + return defaultValue; + } + + String s = valueString.trim(); + if (s.length() == 0) { + return defaultValue; + } + + if (s.startsWith("-")/* negative */) { //$NON-NLS-1$ + throw notTimeUnit(section, subsection, name, valueString); + } + + Matcher m = Pattern.compile("^(0|[1-9][0-9]*)\\s*(.*)$") //$NON-NLS-1$ + .matcher(valueString); + if (!m.matches()) { + return defaultValue; + } + + String digits = m.group(1); + String unitName = m.group(2).trim(); + + TimeUnit inputUnit; + int inputMul; + + if (unitName.isEmpty()) { + inputUnit = wantUnit; + inputMul = 1; + + } else if (match(unitName, "ms", "milliseconds")) { //$NON-NLS-1$ //$NON-NLS-2$ + inputUnit = TimeUnit.MILLISECONDS; + inputMul = 1; + + } else if (match(unitName, "s", "sec", "second", "seconds")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + inputUnit = TimeUnit.SECONDS; + inputMul = 1; + + } else if (match(unitName, "m", "min", "minute", "minutes")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + inputUnit = TimeUnit.MINUTES; + inputMul = 1; + + } else if (match(unitName, "h", "hr", "hour", "hours")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + inputUnit = TimeUnit.HOURS; + inputMul = 1; + + } else if (match(unitName, "d", "day", "days")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + inputUnit = TimeUnit.DAYS; + inputMul = 1; + + } else if (match(unitName, "w", "week", "weeks")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + inputUnit = TimeUnit.DAYS; + inputMul = 7; + + } else if (match(unitName, "mon", "month", "months")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + inputUnit = TimeUnit.DAYS; + inputMul = 30; + + } else if (match(unitName, "y", "year", "years")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + inputUnit = TimeUnit.DAYS; + inputMul = 365; + + } else { + throw notTimeUnit(section, subsection, name, valueString); + } + + try { + return wantUnit.convert(Long.parseLong(digits) * inputMul, + inputUnit); + } catch (NumberFormatException nfe) { + throw notTimeUnit(section, subsection, unitName, valueString); + } + } + + private static boolean match(final String a, final String... cases) { + for (final String b : cases) { + if (b != null && b.equalsIgnoreCase(a)) { + return true; + } + } + return false; + } + + private static IllegalArgumentException notTimeUnit(String section, + String subsection, String name, String valueString) { + if (subsection != null) { + return new IllegalArgumentException( + MessageFormat.format(JGitText.get().invalidTimeUnitValue3, + section, subsection, name, valueString)); + } + return new IllegalArgumentException( + MessageFormat.format(JGitText.get().invalidTimeUnitValue2, + section, name, valueString)); + } + + @Override + public @NonNull List<RefSpec> getRefSpecs(Config config, String section, + String subsection, String name) { + String[] values = config.getStringList(section, subsection, name); + List<RefSpec> result = new ArrayList<>(values.length); + for (String spec : values) { + result.add(new RefSpec(spec)); + } + return result; + } +} 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 a489461f84..edbc709f48 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileMode.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileMode.java @@ -83,7 +83,6 @@ public abstract class FileMode { public static final int TYPE_MISSING = 0000000; /** Mode indicating an entry is a tree (aka directory). */ - @SuppressWarnings("synthetic-access") public static final FileMode TREE = new FileMode(TYPE_TREE, Constants.OBJ_TREE) { @Override @@ -93,7 +92,6 @@ public abstract class FileMode { }; /** Mode indicating an entry is a symbolic link. */ - @SuppressWarnings("synthetic-access") public static final FileMode SYMLINK = new FileMode(TYPE_SYMLINK, Constants.OBJ_BLOB) { @Override @@ -103,7 +101,6 @@ public abstract class FileMode { }; /** Mode indicating an entry is a non-executable file. */ - @SuppressWarnings("synthetic-access") public static final FileMode REGULAR_FILE = new FileMode(0100644, Constants.OBJ_BLOB) { @Override @@ -113,7 +110,6 @@ public abstract class FileMode { }; /** Mode indicating an entry is an executable file. */ - @SuppressWarnings("synthetic-access") public static final FileMode EXECUTABLE_FILE = new FileMode(0100755, Constants.OBJ_BLOB) { @Override @@ -123,7 +119,6 @@ public abstract class FileMode { }; /** Mode indicating an entry is a submodule commit in another repository. */ - @SuppressWarnings("synthetic-access") public static final FileMode GITLINK = new FileMode(TYPE_GITLINK, Constants.OBJ_COMMIT) { @Override @@ -133,7 +128,6 @@ public abstract class FileMode { }; /** Mode indicating an entry is missing during parallel walks. */ - @SuppressWarnings("synthetic-access") public static final FileMode MISSING = new FileMode(TYPE_MISSING, Constants.OBJ_BAD) { @Override 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 9d3aee1508..19c5c7eb45 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectChecker.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectChecker.java @@ -359,7 +359,13 @@ public class ObjectChecker { checkTree(id, raw); break; case OBJ_BLOB: - checkBlob(raw); + BlobObjectChecker checker = newBlobObjectChecker(); + if (checker == null) { + checkBlob(raw); + } else { + checker.update(raw, 0, raw.length); + checker.endBlob(id); + } break; default: report(UNKNOWN_TYPE, id, MessageFormat.format( @@ -1067,8 +1073,22 @@ public class ObjectChecker { } /** + * Create a new {@link BlobObjectChecker}. + * + * @return new BlobObjectChecker or null if it's not provided. + * @since 4.9 + */ + @Nullable + public BlobObjectChecker newBlobObjectChecker() { + return null; + } + + /** * Check a blob for errors. * + * <p>This may not be called from PackParser in some cases. Use {@link + * #newBlobObjectChecker} instead. + * * @param raw * the blob data. The array is never modified. * @throws CorruptObjectException diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/TypedConfigGetter.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/TypedConfigGetter.java new file mode 100644 index 0000000000..594edef665 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/TypedConfigGetter.java @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2017, Thomas Wolf <thomas.wolf@paranor.ch> + * 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; + +import java.util.List; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jgit.annotations.NonNull; +import org.eclipse.jgit.transport.RefSpec; + +/** + * Something that knows how to convert plain strings from a git {@link Config} + * to typed values. + * + * @since 4.9 + */ +public interface TypedConfigGetter { + + /** + * Get a boolean value from a git {@link Config}. + * + * @param config + * to get the value from + * @param section + * section the key is grouped within. + * @param subsection + * subsection name, such a remote or branch name. + * @param name + * name of the key to get. + * @param defaultValue + * default value to return if no value was present. + * @return true if any value or defaultValue is true, false for missing or + * explicit false + */ + boolean getBoolean(Config config, String section, String subsection, + String name, boolean defaultValue); + + /** + * Parse an enumeration from a git {@link Config}. + * + * @param <T> + * type of the enumeration object. + * @param config + * to get the value from + * @param all + * all possible values in the enumeration which should be + * recognized. Typically {@code EnumType.values()}. + * @param section + * section the key is grouped within. + * @param subsection + * subsection name, such a remote or branch name. + * @param name + * name of the key to get. + * @param defaultValue + * default value to return if no value was present. + * @return the selected enumeration value, or {@code defaultValue}. + */ + <T extends Enum<?>> T getEnum(Config config, T[] all, String section, + String subsection, String name, T defaultValue); + + /** + * Obtain an integer value from a git {@link Config}. + * + * @param config + * to get the value from + * @param section + * section the key is grouped within. + * @param subsection + * subsection name, such a remote or branch name. + * @param name + * name of the key to get. + * @param defaultValue + * default value to return if no value was present. + * @return an integer value from the configuration, or defaultValue. + */ + int getInt(Config config, String section, String subsection, String name, + int defaultValue); + + /** + * Obtain a long value from a git {@link Config}. + * + * @param config + * to get the value from + * @param section + * section the key is grouped within. + * @param subsection + * subsection name, such a remote or branch name. + * @param name + * name of the key to get. + * @param defaultValue + * default value to return if no value was present. + * @return a long value from the configuration, or defaultValue. + */ + long getLong(Config config, String section, String subsection, String name, + long defaultValue); + + /** + * Parse a numerical time unit, such as "1 minute", from a git + * {@link Config}. + * + * @param config + * to get the value from + * @param section + * section the key is in. + * @param subsection + * subsection the key is in, or null if not in a subsection. + * @param name + * the key name. + * @param defaultValue + * default value to return if no value was present. + * @param wantUnit + * the units of {@code defaultValue} and the return value, as + * well as the units to assume if the value does not contain an + * indication of the units. + * @return the value, or {@code defaultValue} if not set, expressed in + * {@code units}. + */ + long getTimeUnit(Config config, String section, String subsection, + String name, long defaultValue, TimeUnit wantUnit); + + + /** + * Parse a list of {@link RefSpec}s from a git {@link Config}. + * + * @param config + * to get the list from + * @param section + * section the key is in. + * @param subsection + * subsection the key is in, or null if not in a subsection. + * @param name + * the key name. + * @return a possibly empty list of {@link RefSpec}s + */ + @NonNull + List<RefSpec> getRefSpecs(Config config, String section, String subsection, + String name); +} 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 ce14183a56..242d1c48b6 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/JschConfigSessionFactory.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/JschConfigSessionFactory.java @@ -259,6 +259,9 @@ public abstract class JschConfigSessionFactory extends SshSessionFactory { protected JSch getJSch(final OpenSshConfig.Host hc, FS fs) throws JSchException { if (defaultJSch == null) { defaultJSch = createDefaultJSch(fs); + if (defaultJSch.getConfigRepository() == null) { + defaultJSch.setConfigRepository(config); + } for (Object name : defaultJSch.getIdentityNames()) byIdentityFile.put((String) name, defaultJSch); } @@ -272,6 +275,9 @@ public abstract class JschConfigSessionFactory extends SshSessionFactory { if (jsch == null) { jsch = new JSch(); configureJSch(jsch); + if (jsch.getConfigRepository() == null) { + jsch.setConfigRepository(defaultJSch.getConfigRepository()); + } jsch.setHostKeyRepository(defaultJSch.getHostKeyRepository()); jsch.addIdentity(identityKey); byIdentityFile.put(identityKey, jsch); 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 8b7b60da37..b5b532dffd 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/OpenSshConfig.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/OpenSshConfig.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, 2014, 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 @@ -46,32 +46,90 @@ package org.eclipse.jgit.transport; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.ArrayList; -import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; +import java.util.Locale; import java.util.Map; +import java.util.Set; import org.eclipse.jgit.errors.InvalidPatternException; import org.eclipse.jgit.fnmatch.FileNameMatcher; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.util.FS; import org.eclipse.jgit.util.StringUtils; +import org.eclipse.jgit.util.SystemReader; + +import com.jcraft.jsch.ConfigRepository; /** - * Simple configuration parser for the OpenSSH ~/.ssh/config file. + * Fairly complete configuration parser for the OpenSSH ~/.ssh/config file. + * <p> + * JSch does have its own config file parser + * {@link com.jcraft.jsch.OpenSSHConfig} since version 0.1.50, but it has a + * number of problems: + * <ul> + * <li>it splits lines of the format "keyword = value" wrongly: you'd end up + * with the value "= value". + * <li>its "Host" keyword is not case insensitive. + * <li>it doesn't handle quoted values. + * <li>JSch's OpenSSHConfig doesn't monitor for config file changes. + * </ul> + * <p> + * Therefore implement our own parser to read an OpenSSH configuration file. It + * makes the critical options available to {@link SshSessionFactory} via + * {@link Host} objects returned by {@link #lookup(String)}, and implements a + * fully conforming {@link ConfigRepository} providing + * {@link com.jcraft.jsch.ConfigRepository.Config}s via + * {@link #getConfig(String)}. + * </p> + * <p> + * Limitations compared to the full OpenSSH 7.5 parser: + * </p> + * <ul> + * <li>This parser does not handle Match or Include keywords. + * <li>This parser does not do host name canonicalization (Jsch ignores it + * anyway). + * </ul> + * <p> + * Note that OpenSSH's readconf.c is a validating parser; Jsch's + * ConfigRepository OTOH treats all option values as plain strings, so any + * validation must happen in Jsch outside of the parser. Thus this parser does + * not validate option values, except for a few options when constructing a + * {@link Host} object. + * </p> + * <p> + * This config does %-substitutions for the following tokens: + * </p> + * <ul> + * <li>%% - single % + * <li>%C - short-hand for %l%h%p%r. See %p and %r below; the replacement may be + * done partially only and may leave %p or %r or both unreplaced. + * <li>%d - home directory path + * <li>%h - remote host name + * <li>%L - local host name without domain + * <li>%l - FQDN of the local host + * <li>%n - host name as specified in {@link #lookup(String)} + * <li>%p - port number; replaced only if set in the config + * <li>%r - remote user name; replaced only if set in the config + * <li>%u - local user name + * </ul> * <p> - * Since JSch does not (currently) have the ability to parse an OpenSSH - * configuration file this is a simple parser to read that file and make the - * critical options available to {@link SshSessionFactory}. + * If the config doesn't set the port or the remote user name, %p and %r remain + * un-substituted. It's the caller's responsibility to replace them with values + * obtained from the connection URI. %i is not handled; Java has no concept of a + * "user ID". + * </p> */ -public class OpenSshConfig { +public class OpenSshConfig implements ConfigRepository { + /** IANA assigned port number for SSH. */ static final int SSH_PORT = 22; @@ -105,16 +163,31 @@ public class OpenSshConfig { /** The .ssh/config file we read and monitor for updates. */ private final File configFile; - /** Modification time of {@link #configFile} when {@link #hosts} loaded. */ + /** Modification time of {@link #configFile} when it was last loaded. */ private long lastModified; - /** Cached entries read out of the configuration file. */ - private Map<String, Host> hosts; + /** + * Encapsulates entries read out of the configuration file, and + * {@link Host}s created from that. + */ + private static class State { + Map<String, HostEntry> entries = new LinkedHashMap<>(); + Map<String, Host> hosts = new HashMap<>(); + + @Override + @SuppressWarnings("nls") + public String toString() { + return "State [entries=" + entries + ", hosts=" + hosts + "]"; + } + } + + /** State read from the config file, plus {@link Host}s created from it. */ + private State state; OpenSshConfig(final File h, final File cfg) { home = h; configFile = cfg; - hosts = Collections.emptyMap(); + state = new State(); } /** @@ -127,75 +200,81 @@ public class OpenSshConfig { * @return r configuration for the requested name. Never null. */ public Host lookup(final String hostName) { - final Map<String, Host> cache = refresh(); - Host h = cache.get(hostName); - if (h == null) - h = new Host(); - if (h.patternsApplied) + final State cache = refresh(); + Host h = cache.hosts.get(hostName); + if (h != null) { return h; - - for (final Map.Entry<String, Host> e : cache.entrySet()) { - if (!isHostPattern(e.getKey())) - continue; - if (!isHostMatch(e.getKey(), hostName)) - continue; - h.copyFrom(e.getValue()); - } - - if (h.hostName == null) - h.hostName = hostName; - if (h.user == null) - h.user = OpenSshConfig.userName(); - if (h.port == 0) - h.port = OpenSshConfig.SSH_PORT; - if (h.connectionAttempts == 0) - h.connectionAttempts = 1; - h.patternsApplied = true; + } + HostEntry fullConfig = new HostEntry(); + // Initialize with default entries at the top of the file, before the + // first Host block. + fullConfig.merge(cache.entries.get(HostEntry.DEFAULT_NAME)); + for (final Map.Entry<String, HostEntry> e : cache.entries.entrySet()) { + String key = e.getKey(); + if (isHostMatch(key, hostName)) { + fullConfig.merge(e.getValue()); + } + } + fullConfig.substitute(hostName, home); + h = new Host(fullConfig, hostName, home); + cache.hosts.put(hostName, h); return h; } - private synchronized Map<String, Host> refresh() { + private synchronized State refresh() { final long mtime = configFile.lastModified(); if (mtime != lastModified) { - try { - final FileInputStream in = new FileInputStream(configFile); - try { - hosts = parse(in); - } finally { - in.close(); - } - } catch (FileNotFoundException none) { - hosts = Collections.emptyMap(); - } catch (IOException err) { - hosts = Collections.emptyMap(); + State newState = new State(); + try (FileInputStream in = new FileInputStream(configFile)) { + newState.entries = parse(in); + } catch (IOException none) { + // Ignore -- we'll set and return an empty state } lastModified = mtime; + state = newState; } - return hosts; + return state; } - private Map<String, Host> parse(final InputStream in) throws IOException { - final Map<String, Host> m = new LinkedHashMap<>(); + private Map<String, HostEntry> parse(final InputStream in) + throws IOException { + final Map<String, HostEntry> m = new LinkedHashMap<>(); final BufferedReader br = new BufferedReader(new InputStreamReader(in)); - final List<Host> current = new ArrayList<>(4); + final List<HostEntry> current = new ArrayList<>(4); String line; + // The man page doesn't say so, but the OpenSSH parser (readconf.c) + // starts out in active mode and thus always applies any lines that + // occur before the first host block. We gather those options in a + // HostEntry for DEFAULT_NAME. + HostEntry defaults = new HostEntry(); + current.add(defaults); + m.put(HostEntry.DEFAULT_NAME, defaults); + while ((line = br.readLine()) != null) { line = line.trim(); - if (line.length() == 0 || line.startsWith("#")) //$NON-NLS-1$ + if (line.isEmpty() || line.startsWith("#")) { //$NON-NLS-1$ continue; - - final String[] parts = line.split("[ \t]*[= \t]", 2); //$NON-NLS-1$ - final String keyword = parts[0].trim(); - final String argValue = parts[1].trim(); + } + String[] parts = line.split("[ \t]*[= \t]", 2); //$NON-NLS-1$ + // Although the ssh-config man page doesn't say so, the OpenSSH + // parser does allow quoted keywords. + String keyword = dequote(parts[0].trim()); + // man 5 ssh-config says lines had the format "keyword arguments", + // with no indication that arguments were optional. However, let's + // not crap out on missing arguments. See bug 444319. + String argValue = parts.length > 1 ? parts[1].trim() : ""; //$NON-NLS-1$ if (StringUtils.equalsIgnoreCase("Host", keyword)) { //$NON-NLS-1$ current.clear(); - for (final String pattern : argValue.split("[ \t]")) { //$NON-NLS-1$ - final String name = dequote(pattern); - Host c = m.get(name); + for (String name : HostEntry.parseList(argValue)) { + if (name == null || name.isEmpty()) { + // null should not occur, but better be safe than sorry. + continue; + } + HostEntry c = m.get(name); if (c == null) { - c = new Host(); + c = new HostEntry(); m.put(name, c); } current.add(c); @@ -206,57 +285,18 @@ public class OpenSshConfig { if (current.isEmpty()) { // We received an option outside of a Host block. We // don't know who this should match against, so skip. - // continue; } - if (StringUtils.equalsIgnoreCase("HostName", keyword)) { //$NON-NLS-1$ - for (final Host c : current) - if (c.hostName == null) - c.hostName = dequote(argValue); - } else if (StringUtils.equalsIgnoreCase("User", keyword)) { //$NON-NLS-1$ - for (final Host c : current) - if (c.user == null) - c.user = dequote(argValue); - } else if (StringUtils.equalsIgnoreCase("Port", keyword)) { //$NON-NLS-1$ - try { - final int port = Integer.parseInt(dequote(argValue)); - for (final Host c : current) - if (c.port == 0) - c.port = port; - } catch (NumberFormatException nfe) { - // Bad port number. Don't set it. + if (HostEntry.isListKey(keyword)) { + List<String> args = HostEntry.parseList(argValue); + for (HostEntry entry : current) { + entry.setValue(keyword, args); } - } else if (StringUtils.equalsIgnoreCase("IdentityFile", keyword)) { //$NON-NLS-1$ - for (final Host c : current) - if (c.identityFile == null) - c.identityFile = toFile(dequote(argValue)); - } else if (StringUtils.equalsIgnoreCase( - "PreferredAuthentications", keyword)) { //$NON-NLS-1$ - for (final Host c : current) - if (c.preferredAuthentications == null) - c.preferredAuthentications = nows(dequote(argValue)); - } else if (StringUtils.equalsIgnoreCase("BatchMode", keyword)) { //$NON-NLS-1$ - for (final Host c : current) - if (c.batchMode == null) - c.batchMode = yesno(dequote(argValue)); - } else if (StringUtils.equalsIgnoreCase( - "StrictHostKeyChecking", keyword)) { //$NON-NLS-1$ - String value = dequote(argValue); - for (final Host c : current) - if (c.strictHostKeyChecking == null) - c.strictHostKeyChecking = value; - } else if (StringUtils.equalsIgnoreCase( - "ConnectionAttempts", keyword)) { //$NON-NLS-1$ - try { - final int connectionAttempts = Integer.parseInt(dequote(argValue)); - if (connectionAttempts > 0) { - for (final Host c : current) - if (c.connectionAttempts == 0) - c.connectionAttempts = connectionAttempts; - } - } catch (NumberFormatException nfe) { - // ignore bad values + } else if (!argValue.isEmpty()) { + argValue = dequote(argValue); + for (HostEntry entry : current) { + entry.setValue(keyword, argValue); } } } @@ -264,23 +304,35 @@ public class OpenSshConfig { return m; } - private static boolean isHostPattern(final String s) { - return s.indexOf('*') >= 0 || s.indexOf('?') >= 0; + private static boolean isHostMatch(final String pattern, + final String name) { + if (pattern.startsWith("!")) { //$NON-NLS-1$ + return !patternMatchesHost(pattern.substring(1), name); + } else { + return patternMatchesHost(pattern, name); + } } - private static boolean isHostMatch(final String pattern, final String name) { - final FileNameMatcher fn; - try { - fn = new FileNameMatcher(pattern, null); - } catch (InvalidPatternException e) { - return false; + private static boolean patternMatchesHost(final String pattern, + final String name) { + if (pattern.indexOf('*') >= 0 || pattern.indexOf('?') >= 0) { + final FileNameMatcher fn; + try { + fn = new FileNameMatcher(pattern, null); + } catch (InvalidPatternException e) { + return false; + } + fn.append(name); + return fn.isMatch(); + } else { + // Not a pattern but a full host name + return pattern.equals(name); } - fn.append(name); - return fn.isMatch(); } private static String dequote(final String value) { - if (value.startsWith("\"") && value.endsWith("\"")) //$NON-NLS-1$ //$NON-NLS-2$ + if (value.startsWith("\"") && value.endsWith("\"") //$NON-NLS-1$ //$NON-NLS-2$ + && value.length() > 1) return value.substring(1, value.length() - 1); return value; } @@ -300,24 +352,419 @@ public class OpenSshConfig { return Boolean.FALSE; } - private File toFile(final String path) { - if (path.startsWith("~/")) //$NON-NLS-1$ - return new File(home, path.substring(2)); - File ret = new File(path); - if (ret.isAbsolute()) - return ret; - return new File(home, path); + private static int positive(final String value) { + if (value != null) { + try { + return Integer.parseUnsignedInt(value); + } catch (NumberFormatException e) { + // Ignore + } + } + return -1; } static String userName() { return AccessController.doPrivileged(new PrivilegedAction<String>() { @Override public String run() { - return System.getProperty("user.name"); //$NON-NLS-1$ + return SystemReader.getInstance() + .getProperty(Constants.OS_USER_NAME_KEY); } }); } + private static class HostEntry implements ConfigRepository.Config { + + /** + * "Host name" of the HostEntry for the default options before the first + * host block in a config file. + */ + public static final String DEFAULT_NAME = ""; //$NON-NLS-1$ + + // See com.jcraft.jsch.OpenSSHConfig. Translates some command-line keys + // to ssh-config keys. + private static final Map<String, String> KEY_MAP = new HashMap<>(); + + static { + KEY_MAP.put("kex", "KexAlgorithms"); //$NON-NLS-1$//$NON-NLS-2$ + KEY_MAP.put("server_host_key", "HostKeyAlgorithms"); //$NON-NLS-1$ //$NON-NLS-2$ + KEY_MAP.put("cipher.c2s", "Ciphers"); //$NON-NLS-1$ //$NON-NLS-2$ + KEY_MAP.put("cipher.s2c", "Ciphers"); //$NON-NLS-1$ //$NON-NLS-2$ + KEY_MAP.put("mac.c2s", "Macs"); //$NON-NLS-1$ //$NON-NLS-2$ + KEY_MAP.put("mac.s2c", "Macs"); //$NON-NLS-1$ //$NON-NLS-2$ + KEY_MAP.put("compression.s2c", "Compression"); //$NON-NLS-1$ //$NON-NLS-2$ + KEY_MAP.put("compression.c2s", "Compression"); //$NON-NLS-1$ //$NON-NLS-2$ + KEY_MAP.put("compression_level", "CompressionLevel"); //$NON-NLS-1$ //$NON-NLS-2$ + KEY_MAP.put("MaxAuthTries", "NumberOfPasswordPrompts"); //$NON-NLS-1$ //$NON-NLS-2$ + } + + /** + * Keys that can be specified multiple times, building up a list. (I.e., + * those are the keys that do not follow the general rule of "first + * occurrence wins".) + */ + private static final Set<String> MULTI_KEYS = new HashSet<>(); + + static { + MULTI_KEYS.add("CERTIFICATEFILE"); //$NON-NLS-1$ + MULTI_KEYS.add("IDENTITYFILE"); //$NON-NLS-1$ + MULTI_KEYS.add("LOCALFORWARD"); //$NON-NLS-1$ + MULTI_KEYS.add("REMOTEFORWARD"); //$NON-NLS-1$ + MULTI_KEYS.add("SENDENV"); //$NON-NLS-1$ + } + + /** + * Keys that take a whitespace-separated list of elements as argument. + * Because the dequote-handling is different, we must handle those in + * the parser. There are a few other keys that take comma-separated + * lists as arguments, but for the parser those are single arguments + * that must be quoted if they contain whitespace, and taking them apart + * is the responsibility of the user of those keys. + */ + private static final Set<String> LIST_KEYS = new HashSet<>(); + + static { + LIST_KEYS.add("CANONICALDOMAINS"); //$NON-NLS-1$ + LIST_KEYS.add("GLOBALKNOWNHOSTSFILE"); //$NON-NLS-1$ + LIST_KEYS.add("SENDENV"); //$NON-NLS-1$ + LIST_KEYS.add("USERKNOWNHOSTSFILE"); //$NON-NLS-1$ + } + + private Map<String, String> options; + + private Map<String, List<String>> multiOptions; + + private Map<String, List<String>> listOptions; + + @Override + public String getHostname() { + return getValue("HOSTNAME"); //$NON-NLS-1$ + } + + @Override + public String getUser() { + return getValue("USER"); //$NON-NLS-1$ + } + + @Override + public int getPort() { + return positive(getValue("PORT")); //$NON-NLS-1$ + } + + private static String mapKey(String key) { + String k = KEY_MAP.get(key); + if (k == null) { + k = key; + } + return k.toUpperCase(Locale.ROOT); + } + + private String findValue(String key) { + String k = mapKey(key); + String result = options != null ? options.get(k) : null; + if (result == null) { + // Also check the list and multi options. Modern OpenSSH treats + // UserKnownHostsFile and GlobalKnownHostsFile as list-valued, + // and so does this parser. Jsch 0.1.54 in general doesn't know + // about list-valued options (it _does_ know multi-valued + // options, though), and will ask for a single value for such + // options. + // + // Let's be lenient and return at least the first value from + // a list-valued or multi-valued key for which Jsch asks for a + // single value. + List<String> values = listOptions != null ? listOptions.get(k) + : null; + if (values == null) { + values = multiOptions != null ? multiOptions.get(k) : null; + } + if (values != null && !values.isEmpty()) { + result = values.get(0); + } + } + return result; + } + + @Override + public String getValue(String key) { + // See com.jcraft.jsch.OpenSSHConfig.MyConfig.getValue() for this + // special case. + if (key.equals("compression.s2c") //$NON-NLS-1$ + || key.equals("compression.c2s")) { //$NON-NLS-1$ + String foo = findValue(key); + if (foo == null || foo.equals("no")) { //$NON-NLS-1$ + return "none,zlib@openssh.com,zlib"; //$NON-NLS-1$ + } + return "zlib@openssh.com,zlib,none"; //$NON-NLS-1$ + } + return findValue(key); + } + + @Override + public String[] getValues(String key) { + String k = mapKey(key); + List<String> values = listOptions != null ? listOptions.get(k) + : null; + if (values == null) { + values = multiOptions != null ? multiOptions.get(k) : null; + } + if (values == null || values.isEmpty()) { + return new String[0]; + } + return values.toArray(new String[values.size()]); + } + + public void setValue(String key, String value) { + String k = key.toUpperCase(Locale.ROOT); + if (MULTI_KEYS.contains(k)) { + if (multiOptions == null) { + multiOptions = new HashMap<>(); + } + List<String> values = multiOptions.get(k); + if (values == null) { + values = new ArrayList<>(4); + multiOptions.put(k, values); + } + values.add(value); + } else { + if (options == null) { + options = new HashMap<>(); + } + if (!options.containsKey(k)) { + options.put(k, value); + } + } + } + + public void setValue(String key, List<String> values) { + if (values.isEmpty()) { + // Can occur only on a missing argument: ignore. + return; + } + String k = key.toUpperCase(Locale.ROOT); + // Check multi-valued keys first; because of the replacement + // strategy, they must take precedence over list-valued keys + // which always follow the "first occurrence wins" strategy. + // + // Note that SendEnv is a multi-valued list-valued key. (It's + // rather immaterial for JGit, though.) + if (MULTI_KEYS.contains(k)) { + if (multiOptions == null) { + multiOptions = new HashMap<>(2 * MULTI_KEYS.size()); + } + List<String> items = multiOptions.get(k); + if (items == null) { + items = new ArrayList<>(values); + multiOptions.put(k, items); + } else { + items.addAll(values); + } + } else { + if (listOptions == null) { + listOptions = new HashMap<>(2 * LIST_KEYS.size()); + } + if (!listOptions.containsKey(k)) { + listOptions.put(k, values); + } + } + } + + public static boolean isListKey(String key) { + return LIST_KEYS.contains(key.toUpperCase(Locale.ROOT)); + } + + /** + * Splits the argument into a list of whitespace-separated elements. + * Elements containing whitespace must be quoted and will be de-quoted. + * + * @param argument + * argument part of the configuration line as read from the + * config file + * @return a {@link List} of elements, possibly empty and possibly + * containing empty elements + */ + public static List<String> parseList(String argument) { + List<String> result = new ArrayList<>(4); + int start = 0; + int length = argument.length(); + while (start < length) { + // Skip whitespace + if (Character.isSpaceChar(argument.charAt(start))) { + start++; + continue; + } + if (argument.charAt(start) == '"') { + int stop = argument.indexOf('"', ++start); + if (stop < start) { + // No closing double quote: skip + break; + } + result.add(argument.substring(start, stop)); + start = stop + 1; + } else { + int stop = start + 1; + while (stop < length + && !Character.isSpaceChar(argument.charAt(stop))) { + stop++; + } + result.add(argument.substring(start, stop)); + start = stop + 1; + } + } + return result; + } + + protected void merge(HostEntry entry) { + if (entry == null) { + // Can occur if we could not read the config file + return; + } + if (entry.options != null) { + if (options == null) { + options = new HashMap<>(); + } + for (Map.Entry<String, String> item : entry.options + .entrySet()) { + if (!options.containsKey(item.getKey())) { + options.put(item.getKey(), item.getValue()); + } + } + } + if (entry.listOptions != null) { + if (listOptions == null) { + listOptions = new HashMap<>(2 * LIST_KEYS.size()); + } + for (Map.Entry<String, List<String>> item : entry.listOptions + .entrySet()) { + if (!listOptions.containsKey(item.getKey())) { + listOptions.put(item.getKey(), item.getValue()); + } + } + + } + if (entry.multiOptions != null) { + if (multiOptions == null) { + multiOptions = new HashMap<>(2 * MULTI_KEYS.size()); + } + for (Map.Entry<String, List<String>> item : entry.multiOptions + .entrySet()) { + List<String> values = multiOptions.get(item.getKey()); + if (values == null) { + values = new ArrayList<>(item.getValue()); + multiOptions.put(item.getKey(), values); + } else { + values.addAll(item.getValue()); + } + } + } + } + + private class Replacer { + private final Map<Character, String> replacements = new HashMap<>(); + + public Replacer(String originalHostName, File home) { + replacements.put(Character.valueOf('%'), "%"); //$NON-NLS-1$ + replacements.put(Character.valueOf('d'), home.getPath()); + // Needs special treatment... + String host = getValue("HOSTNAME"); //$NON-NLS-1$ + replacements.put(Character.valueOf('h'), originalHostName); + if (host != null && host.indexOf('%') >= 0) { + host = substitute(host, "h"); //$NON-NLS-1$ + options.put("HOSTNAME", host); //$NON-NLS-1$ + } + if (host != null) { + replacements.put(Character.valueOf('h'), host); + } + String localhost = SystemReader.getInstance().getHostname(); + replacements.put(Character.valueOf('l'), localhost); + int period = localhost.indexOf('.'); + if (period > 0) { + localhost = localhost.substring(0, period); + } + replacements.put(Character.valueOf('L'), localhost); + replacements.put(Character.valueOf('n'), originalHostName); + replacements.put(Character.valueOf('p'), getValue("PORT")); //$NON-NLS-1$ + replacements.put(Character.valueOf('r'), getValue("USER")); //$NON-NLS-1$ + replacements.put(Character.valueOf('u'), userName()); + replacements.put(Character.valueOf('C'), + substitute("%l%h%p%r", "hlpr")); //$NON-NLS-1$ //$NON-NLS-2$ + } + + public String substitute(String input, String allowed) { + if (input == null || input.length() <= 1 + || input.indexOf('%') < 0) { + return input; + } + StringBuilder builder = new StringBuilder(); + int start = 0; + int length = input.length(); + while (start < length) { + int percent = input.indexOf('%', start); + if (percent < 0 || percent + 1 >= length) { + builder.append(input.substring(start)); + break; + } + String replacement = null; + char ch = input.charAt(percent + 1); + if (ch == '%' || allowed.indexOf(ch) >= 0) { + replacement = replacements.get(Character.valueOf(ch)); + } + if (replacement == null) { + builder.append(input.substring(start, percent + 2)); + } else { + builder.append(input.substring(start, percent)) + .append(replacement); + } + start = percent + 2; + } + return builder.toString(); + } + } + + private List<String> substitute(List<String> values, String allowed, + Replacer r) { + List<String> result = new ArrayList<>(values.size()); + for (String value : values) { + result.add(r.substitute(value, allowed)); + } + return result; + } + + protected void substitute(String originalHostName, File home) { + Replacer r = new Replacer(originalHostName, home); + if (multiOptions != null) { + List<String> values = multiOptions.get("IDENTITYFILE"); //$NON-NLS-1$ + if (values != null) { + values = substitute(values, "dhlru", r); //$NON-NLS-1$ + multiOptions.put("IDENTITYFILE", values); //$NON-NLS-1$ + } + values = multiOptions.get("CERTIFICATEFILE"); //$NON-NLS-1$ + if (values != null) { + values = substitute(values, "dhlru", r); //$NON-NLS-1$ + multiOptions.put("CERTIFICATEFILE", values); //$NON-NLS-1$ + } + } + if (options != null) { + // HOSTNAME already done in Replacer constructor + String value = options.get("IDENTITYAGENT"); //$NON-NLS-1$ + if (value != null) { + value = r.substitute(value, "dhlru"); //$NON-NLS-1$ + options.put("IDENTITYAGENT", value); //$NON-NLS-1$ + } + } + // Match is not implemented and would need to be done elsewhere + // anyway. ControlPath, LocalCommand, ProxyCommand, and + // RemoteCommand are not used by Jsch. + } + + @Override + @SuppressWarnings("nls") + public String toString() { + return "HostEntry [options=" + options + ", multiOptions=" + + multiOptions + ", listOptions=" + listOptions + "]"; + } + } + /** * Configuration of one "Host" block in the configuration file. * <p> @@ -330,8 +777,6 @@ public class OpenSshConfig { * already merged into this block. */ public static class Host { - boolean patternsApplied; - String hostName; int port; @@ -348,23 +793,18 @@ public class OpenSshConfig { int connectionAttempts; - void copyFrom(final Host src) { - if (hostName == null) - hostName = src.hostName; - if (port == 0) - port = src.port; - if (identityFile == null) - identityFile = src.identityFile; - if (user == null) - user = src.user; - if (preferredAuthentications == null) - preferredAuthentications = src.preferredAuthentications; - if (batchMode == null) - batchMode = src.batchMode; - if (strictHostKeyChecking == null) - strictHostKeyChecking = src.strictHostKeyChecking; - if (connectionAttempts == 0) - connectionAttempts = src.connectionAttempts; + private Config config; + + /** + * Creates a new uninitialized {@link Host}. + */ + public Host() { + // For API backwards compatibility with pre-4.9 JGit + } + + Host(Config config, String hostName, File homeDir) { + this.config = config; + complete(hostName, homeDir); } /** @@ -432,5 +872,89 @@ public class OpenSshConfig { public int getConnectionAttempts() { return connectionAttempts; } + + + private void complete(String initialHostName, File homeDir) { + // Try to set values from the options. + hostName = config.getHostname(); + user = config.getUser(); + port = config.getPort(); + connectionAttempts = positive( + config.getValue("ConnectionAttempts")); //$NON-NLS-1$ + strictHostKeyChecking = config.getValue("StrictHostKeyChecking"); //$NON-NLS-1$ + String value = config.getValue("BatchMode"); //$NON-NLS-1$ + if (value != null) { + batchMode = yesno(value); + } + value = config.getValue("PreferredAuthentications"); //$NON-NLS-1$ + if (value != null) { + preferredAuthentications = nows(value); + } + // Fill in defaults if still not set + if (hostName == null) { + hostName = initialHostName; + } + if (user == null) { + user = OpenSshConfig.userName(); + } + if (port <= 0) { + port = OpenSshConfig.SSH_PORT; + } + if (connectionAttempts <= 0) { + connectionAttempts = 1; + } + String[] identityFiles = config.getValues("IdentityFile"); //$NON-NLS-1$ + if (identityFiles != null && identityFiles.length > 0) { + identityFile = toFile(identityFiles[0], homeDir); + } + } + + private File toFile(String path, File home) { + if (path.startsWith("~/")) { //$NON-NLS-1$ + return new File(home, path.substring(2)); + } + File ret = new File(path); + if (ret.isAbsolute()) { + return ret; + } + return new File(home, path); + } + + Config getConfig() { + return config; + } + + @Override + @SuppressWarnings("nls") + public String toString() { + return "Host [hostName=" + hostName + ", port=" + port + + ", identityFile=" + identityFile + ", user=" + user + + ", preferredAuthentications=" + preferredAuthentications + + ", batchMode=" + batchMode + ", strictHostKeyChecking=" + + strictHostKeyChecking + ", connectionAttempts=" + + connectionAttempts + ", config=" + config + "]"; + } + } + + /** + * Retrieves the full {@link com.jcraft.jsch.ConfigRepository.Config Config} + * for the given host name. + * + * @param hostName + * to get the config for + * @return the configuration for the host + * @since 4.9 + */ + @Override + public Config getConfig(String hostName) { + Host host = lookup(hostName); + return host.getConfig(); + } + + @Override + @SuppressWarnings("nls") + public String toString() { + return "OpenSshConfig [home=" + home + ", configFile=" + configFile + + ", lastModified=" + lastModified + ", state=" + state + "]"; } } 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 2f6b271d87..833d2114cf 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PackParser.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PackParser.java @@ -66,6 +66,7 @@ import org.eclipse.jgit.internal.storage.file.PackLock; import org.eclipse.jgit.internal.storage.pack.BinaryDelta; import org.eclipse.jgit.lib.AnyObjectId; import org.eclipse.jgit.lib.BatchingProgressMonitor; +import org.eclipse.jgit.lib.BlobObjectChecker; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.InflaterCache; import org.eclipse.jgit.lib.MutableObjectId; @@ -1043,16 +1044,25 @@ public abstract class PackParser { if (type == Constants.OBJ_BLOB) { byte[] readBuffer = buffer(); InputStream inf = inflate(Source.INPUT, sz); + BlobObjectChecker checker = null; + if (objCheck != null) { + checker = objCheck.newBlobObjectChecker(); + } + if (checker == null) { + checker = BlobObjectChecker.NULL_CHECKER; + } long cnt = 0; while (cnt < sz) { int r = inf.read(readBuffer); if (r <= 0) break; objectDigest.update(readBuffer, 0, r); + checker.update(readBuffer, 0, r); cnt += r; } inf.close(); objectDigest.digest(tempObjectId); + checker.endBlob(tempObjectId); data = null; } else { data = inflateAndReturn(Source.INPUT, sz); 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 a0d81c00f0..c968ba37cf 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteConfig.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteConfig.java @@ -192,28 +192,27 @@ public class RemoteConfig implements Serializable { } } } - vlst = rc.getStringList(SECTION, name, KEY_FETCH); - 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<>(vlst.length); - for (final String s : vlst) - push.add(new RefSpec(s)); - + fetch = rc.getRefSpecs(SECTION, name, KEY_FETCH); + push = rc.getRefSpecs(SECTION, name, KEY_PUSH); val = rc.getString(SECTION, name, KEY_UPLOADPACK); - if (val == null) + if (val == null) { val = DEFAULT_UPLOAD_PACK; + } uploadpack = val; val = rc.getString(SECTION, name, KEY_RECEIVEPACK); - if (val == null) + if (val == null) { val = DEFAULT_RECEIVE_PACK; + } receivepack = val; - val = rc.getString(SECTION, name, KEY_TAGOPT); - tagopt = TagOpt.fromOption(val); + try { + val = rc.getString(SECTION, name, KEY_TAGOPT); + tagopt = TagOpt.fromOption(val); + } catch (IllegalArgumentException e) { + // C git silently ignores invalid tagopt values. + tagopt = TagOpt.AUTO_FOLLOW; + } mirror = rc.getBoolean(SECTION, name, KEY_MIRROR, DEFAULT_MIRROR); timeout = rc.getInt(SECTION, name, KEY_TIMEOUT, 0); } 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 327dc39c7f..26612120c1 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java @@ -323,6 +323,13 @@ public class TransportHttp extends HttpTransport implements WalkTransport, } } + /** + * The current URI we're talking to. The inherited (final) field + * {@link #uri} stores the original URI; {@code currentUri} may be different + * after redirects. + */ + private URIish currentUri; + private URL baseUrl; private URL objectsUrl; @@ -360,6 +367,7 @@ public class TransportHttp extends HttpTransport implements WalkTransport, */ protected void setURI(final URIish uri) throws NotSupportedException { try { + currentUri = uri; baseUrl = toURL(uri); objectsUrl = new URL(baseUrl, "objects/"); //$NON-NLS-1$ } catch (MalformedURLException e) { @@ -584,9 +592,10 @@ public class TransportHttp extends HttpTransport implements WalkTransport, throw new TransportException(uri, JGitText.get().noCredentialsProvider); if (authAttempts > 1) - credentialsProvider.reset(uri); + credentialsProvider.reset(currentUri); if (3 < authAttempts - || !authMethod.authorize(uri, credentialsProvider)) { + || !authMethod.authorize(currentUri, + credentialsProvider)) { throw new TransportException(uri, JGitText.get().notAuthorized); } @@ -595,7 +604,8 @@ public class TransportHttp extends HttpTransport implements WalkTransport, case HttpConnection.HTTP_FORBIDDEN: throw new TransportException(uri, MessageFormat.format( - JGitText.get().serviceNotPermitted, service)); + JGitText.get().serviceNotPermitted, baseUrl, + service)); case HttpConnection.HTTP_MOVED_PERM: case HttpConnection.HTTP_MOVED_TEMP: @@ -605,9 +615,10 @@ public class TransportHttp extends HttpTransport implements WalkTransport, // and in general should occur only on POST requests. But it // doesn't hurt to accept it here as a redirect. if (http.followRedirects == HttpRedirectMode.FALSE) { - throw new TransportException(MessageFormat.format( - JGitText.get().redirectsOff, uri, - Integer.valueOf(status))); + throw new TransportException(uri, + MessageFormat.format( + JGitText.get().redirectsOff, + Integer.valueOf(status))); } URIish newUri = redirect(conn.getHeaderField(HDR_LOCATION), Constants.INFO_REFS, redirects++); @@ -646,31 +657,34 @@ public class TransportHttp extends HttpTransport implements WalkTransport, private URIish redirect(String location, String checkFor, int redirects) throws TransportException { if (location == null || location.isEmpty()) { - throw new TransportException(MessageFormat.format( - JGitText.get().redirectLocationMissing, uri, baseUrl)); + throw new TransportException(uri, + MessageFormat.format(JGitText.get().redirectLocationMissing, + baseUrl)); } if (redirects >= http.maxRedirects) { - throw new TransportException(MessageFormat.format( - JGitText.get().redirectLimitExceeded, uri, + throw new TransportException(uri, + MessageFormat.format(JGitText.get().redirectLimitExceeded, Integer.valueOf(http.maxRedirects), baseUrl, location)); } try { if (!isValidRedirect(baseUrl, location, checkFor)) { - throw new TransportException( + throw new TransportException(uri, MessageFormat.format(JGitText.get().redirectBlocked, - uri, baseUrl, location)); + baseUrl, location)); } location = location.substring(0, location.indexOf(checkFor)); URIish result = new URIish(location); if (LOG.isInfoEnabled()) { - LOG.info(MessageFormat.format(JGitText.get().redirectHttp, uri, + LOG.info(MessageFormat.format(JGitText.get().redirectHttp, + uri.setPass(null), Integer.valueOf(redirects), baseUrl, result)); } return result; } catch (URISyntaxException e) { - throw new TransportException(MessageFormat.format( - JGitText.get().invalidRedirectLocation, - uri, baseUrl, location), e); + throw new TransportException(uri, + MessageFormat.format(JGitText.get().invalidRedirectLocation, + baseUrl, location), + e); } } @@ -1096,8 +1110,17 @@ public class TransportHttp extends HttpTransport implements WalkTransport, buf = out; } + HttpAuthMethod authenticator = null; + Collection<Type> ignoreTypes = EnumSet.noneOf(Type.class); + // Counts number of repeated authentication attempts using the same + // authentication scheme + int authAttempts = 1; int redirects = 0; for (;;) { + // The very first time we will try with the authentication + // method used on the initial GET request. This is a hint only; + // it may fail. If so, we'll then re-try with proper 401 + // handling, going through the available authentication schemes. openStream(); if (buf != out) { conn.setRequestProperty(HDR_CONTENT_ENCODING, ENCODING_GZIP); @@ -1107,31 +1130,111 @@ public class TransportHttp extends HttpTransport implements WalkTransport, buf.writeTo(httpOut, null); } - if (http.followRedirects == HttpRedirectMode.TRUE) { - final int status = HttpSupport.response(conn); - switch (status) { - case HttpConnection.HTTP_MOVED_PERM: - case HttpConnection.HTTP_MOVED_TEMP: - case HttpConnection.HTTP_11_MOVED_TEMP: - // SEE_OTHER after a POST doesn't make sense for a git - // server, so we don't handle it here and thus we'll - // report an error in openResponse() later on. - URIish newUri = redirect( - conn.getHeaderField(HDR_LOCATION), - '/' + serviceName, redirects++); - try { - baseUrl = toURL(newUri); - } catch (MalformedURLException e) { - throw new TransportException(MessageFormat.format( - JGitText.get().invalidRedirectLocation, - uri, baseUrl, newUri), e); + final int status = HttpSupport.response(conn); + switch (status) { + case HttpConnection.HTTP_OK: + // We're done. + return; + + case HttpConnection.HTTP_NOT_FOUND: + throw new NoRemoteRepositoryException(uri, MessageFormat + .format(JGitText.get().uriNotFound, conn.getURL())); + + case HttpConnection.HTTP_FORBIDDEN: + throw new TransportException(uri, + MessageFormat.format( + JGitText.get().serviceNotPermitted, + baseUrl, serviceName)); + + case HttpConnection.HTTP_MOVED_PERM: + case HttpConnection.HTTP_MOVED_TEMP: + case HttpConnection.HTTP_11_MOVED_TEMP: + // SEE_OTHER after a POST doesn't make sense for a git + // server, so we don't handle it here and thus we'll + // report an error in openResponse() later on. + if (http.followRedirects != HttpRedirectMode.TRUE) { + // Let openResponse() issue an error + return; + } + currentUri = redirect( + conn.getHeaderField(HDR_LOCATION), + '/' + serviceName, redirects++); + try { + baseUrl = toURL(currentUri); + } catch (MalformedURLException e) { + throw new TransportException(uri, MessageFormat.format( + JGitText.get().invalidRedirectLocation, + baseUrl, currentUri), e); + } + continue; + + case HttpConnection.HTTP_UNAUTHORIZED: + HttpAuthMethod nextMethod = HttpAuthMethod + .scanResponse(conn, ignoreTypes); + switch (nextMethod.getType()) { + case NONE: + throw new TransportException(uri, + MessageFormat.format( + JGitText.get().authenticationNotSupported, + conn.getURL())); + case NEGOTIATE: + // RFC 4559 states "When using the SPNEGO [...] with + // [...] POST, the authentication should be complete + // [...] before sending the user data." So in theory + // the initial GET should have been authenticated + // already. (Unless there was a redirect?) + // + // We try this only once: + ignoreTypes.add(HttpAuthMethod.Type.NEGOTIATE); + if (authenticator != null) { + ignoreTypes.add(authenticator.getType()); } - continue; + authAttempts = 1; + // We only do the Kerberos part of SPNEGO, which + // requires only one attempt. We do *not* to the + // NTLM part of SPNEGO; it's a multi-round + // negotiation and among other problems it would + // be unclear when to stop if no HTTP_OK is + // forthcoming. In theory a malicious server + // could keep sending requests for another NTLM + // round, keeping a client stuck here. + break; default: + // DIGEST or BASIC. Let's be sure we ignore NEGOTIATE; + // if it was available, we have tried it before. + ignoreTypes.add(HttpAuthMethod.Type.NEGOTIATE); + if (authenticator == null || authenticator + .getType() != nextMethod.getType()) { + if (authenticator != null) { + ignoreTypes.add(authenticator.getType()); + } + authAttempts = 1; + } break; } + authMethod = nextMethod; + authenticator = nextMethod; + CredentialsProvider credentialsProvider = getCredentialsProvider(); + if (credentialsProvider == null) { + throw new TransportException(uri, + JGitText.get().noCredentialsProvider); + } + if (authAttempts > 1) { + credentialsProvider.reset(currentUri); + } + if (3 < authAttempts || !authMethod.authorize(currentUri, + credentialsProvider)) { + throw new TransportException(uri, + JGitText.get().notAuthorized); + } + authAttempts++; + continue; + + default: + // Just return here; openResponse() will report an appropriate + // error. + return; } - break; } } 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 17af0b9838..c3f50a4d1b 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java @@ -719,7 +719,7 @@ public class UploadPack { } private void service() throws IOException { - boolean sendPack; + boolean sendPack = false; // If it's a non-bidi request, we need to read the entire request before // writing a response. Buffer the response until then. try { @@ -752,6 +752,17 @@ public class UploadPack { if (!clientShallowCommits.isEmpty()) walk.assumeShallow(clientShallowCommits); sendPack = negotiate(); + if (sendPack && !biDirectionalPipe) { + // Ensure the request was fully consumed. Any remaining input must + // be a protocol error. If we aren't at EOF the implementation is broken. + int eof = rawIn.read(); + if (0 <= eof) { + sendPack = false; + throw new CorruptObjectException(MessageFormat.format( + JGitText.get().expectedEOFReceived, + "\\x" + Integer.toHexString(eof))); //$NON-NLS-1$ + } + } } catch (ServiceMayNotContinueException err) { if (!err.isOutput() && err.getMessage() != null) { try { @@ -778,6 +789,11 @@ public class UploadPack { } throw err; } finally { + if (!sendPack && !biDirectionalPipe) { + while (0 < rawIn.skip(2048) || 0 <= rawIn.read()) { + // Discard until EOF. + } + } rawOut.stopBuffering(); } @@ -1390,17 +1406,6 @@ public class UploadPack { private void sendPack() throws IOException { final boolean sideband = options.contains(OPTION_SIDE_BAND) || options.contains(OPTION_SIDE_BAND_64K); - - if (!biDirectionalPipe) { - // Ensure the request was fully consumed. Any remaining input must - // be a protocol error. If we aren't at EOF the implementation is broken. - int eof = rawIn.read(); - if (0 <= eof) - throw new CorruptObjectException(MessageFormat.format( - JGitText.get().expectedEOFReceived, - "\\x" + Integer.toHexString(eof))); //$NON-NLS-1$ - } - if (sideband) { try { sendPack(true); 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 0780d2b560..2d58a0241c 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 @@ -188,7 +188,7 @@ public class FS_POSIX extends FS { if (!isFile(f)) return false; if (!canExecute) - return f.setExecutable(false); + return f.setExecutable(false, false); try { Path path = f.toPath(); |