Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShawn O. Pearce2009-09-30 01:18:28 +0000
committerShawn O. Pearce2009-09-30 01:18:28 +0000
commitdfbdc456d8645fc0c310b5e15cf8d25d8ff7f84b (patch)
tree73461cd1d4e0481e18d25b0c75bd2a4b6d130e45 /org.eclipse.egit.core
downloadegit-dfbdc456d8645fc0c310b5e15cf8d25d8ff7f84b.tar.gz
egit-dfbdc456d8645fc0c310b5e15cf8d25d8ff7f84b.tar.xz
egit-dfbdc456d8645fc0c310b5e15cf8d25d8ff7f84b.zip
Initial EGit contribution to eclipse.org
Per CQ 3393 this is the initial contribution of the EGit project (an Eclipse team provider for Git) to eclipse.org. This initial version is derived from the historial EGit repository at commit a9578ba7361b66ab403c6605a1b87fb7b2f94c6e. Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Diffstat (limited to 'org.eclipse.egit.core')
-rw-r--r--org.eclipse.egit.core/.classpath7
-rw-r--r--org.eclipse.egit.core/.gitignore1
-rw-r--r--org.eclipse.egit.core/.options1
-rw-r--r--org.eclipse.egit.core/.project28
-rw-r--r--org.eclipse.egit.core/.settings/org.eclipse.core.resources.prefs3
-rw-r--r--org.eclipse.egit.core/.settings/org.eclipse.core.runtime.prefs3
-rw-r--r--org.eclipse.egit.core/.settings/org.eclipse.jdt.core.prefs320
-rw-r--r--org.eclipse.egit.core/.settings/org.eclipse.jdt.ui.prefs9
-rw-r--r--org.eclipse.egit.core/META-INF/MANIFEST.MF21
-rw-r--r--org.eclipse.egit.core/build.properties7
-rw-r--r--org.eclipse.egit.core/plugin.properties10
-rw-r--r--org.eclipse.egit.core/plugin.xml20
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/Activator.java102
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/AdaptableFileTreeIterator.java88
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/ContainerTreeIterator.java239
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/CoreText.java106
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/EclipseGitProgressTransformer.java110
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/GitCorePreferenceInitializer.java27
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/GitCorePreferences.java21
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/GitMoveDeleteHook.java205
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/GitProvider.java90
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/GitTag.java34
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/ResourceList.java43
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/coretext.properties51
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/internal/UpdateJob.java164
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/internal/storage/BlobStorage.java81
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/internal/storage/CommitFileRevision.java131
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/internal/storage/GitFileHistory.java187
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/internal/storage/GitFileHistoryProvider.java38
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/internal/storage/GitFileRevision.java91
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/internal/storage/IndexFileRevision.java88
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/internal/storage/KidCommit.java42
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/internal/storage/KidCommitList.java21
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/internal/storage/KidWalk.java26
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/internal/storage/WorkspaceFileRevision.java56
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/internal/util/ExceptionCollector.java128
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/op/AssumeUnchangedOperation.java125
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/op/BranchOperation.java168
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/op/CloneOperation.java221
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/op/ConnectProviderOperation.java101
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/op/DisconnectProviderOperation.java91
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/op/ListRemoteOperation.java105
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/op/PushOperation.java154
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/op/PushOperationResult.java274
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/op/PushOperationSpecification.java83
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/op/ResetOperation.java244
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/op/TrackOperation.java149
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/op/UntrackOperation.java127
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/op/UpdateOperation.java46
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/project/GitProjectData.java491
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/project/RepositoryChangeListener.java28
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/project/RepositoryFinder.java143
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/project/RepositoryMapping.java248
53 files changed, 5397 insertions, 0 deletions
diff --git a/org.eclipse.egit.core/.classpath b/org.eclipse.egit.core/.classpath
new file mode 100644
index 0000000000..304e86186a
--- /dev/null
+++ b/org.eclipse.egit.core/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/org.eclipse.egit.core/.gitignore b/org.eclipse.egit.core/.gitignore
new file mode 100644
index 0000000000..ba077a4031
--- /dev/null
+++ b/org.eclipse.egit.core/.gitignore
@@ -0,0 +1 @@
+bin
diff --git a/org.eclipse.egit.core/.options b/org.eclipse.egit.core/.options
new file mode 100644
index 0000000000..ef2dc61043
--- /dev/null
+++ b/org.eclipse.egit.core/.options
@@ -0,0 +1 @@
+org.eclipse.egit.core/trace/verbose = false
diff --git a/org.eclipse.egit.core/.project b/org.eclipse.egit.core/.project
new file mode 100644
index 0000000000..e937b1cf4d
--- /dev/null
+++ b/org.eclipse.egit.core/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.eclipse.egit.core</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/org.eclipse.egit.core/.settings/org.eclipse.core.resources.prefs b/org.eclipse.egit.core/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000000..d4ff695e8b
--- /dev/null
+++ b/org.eclipse.egit.core/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,3 @@
+#Mon Aug 11 16:47:03 PDT 2008
+eclipse.preferences.version=1
+encoding/<project>=UTF-8
diff --git a/org.eclipse.egit.core/.settings/org.eclipse.core.runtime.prefs b/org.eclipse.egit.core/.settings/org.eclipse.core.runtime.prefs
new file mode 100644
index 0000000000..b6db226b2c
--- /dev/null
+++ b/org.eclipse.egit.core/.settings/org.eclipse.core.runtime.prefs
@@ -0,0 +1,3 @@
+#Mon Mar 24 18:54:53 EDT 2008
+eclipse.preferences.version=1
+line.separator=\n
diff --git a/org.eclipse.egit.core/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.egit.core/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000000..f94f9a80d2
--- /dev/null
+++ b/org.eclipse.egit.core/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,320 @@
+#Sun Feb 03 18:48:30 CET 2008
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.doc.comment.support=enabled
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.autoboxing=warning
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
+org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
+org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=error
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=error
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=warning
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=error
+org.eclipse.jdt.core.compiler.problem.invalidJavadoc=error
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=private
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=error
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingJavadocComments=error
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=protected
+org.eclipse.jdt.core.compiler.problem.missingJavadocTags=error
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=error
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=error
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
+org.eclipse.jdt.core.compiler.problem.nullReference=error
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=warning
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=error
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=ignore
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=error
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=error
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.unusedImport=error
+org.eclipse.jdt.core.compiler.problem.unusedLabel=error
+org.eclipse.jdt.core.compiler.problem.unusedLocal=error
+org.eclipse.jdt.core.compiler.problem.unusedParameter=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=error
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=error
+org.eclipse.jdt.core.compiler.source=1.5
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=0
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=1
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines=false
+org.eclipse.jdt.core.formatter.comment.format_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=false
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert
+org.eclipse.jdt.core.formatter.comment.line_length=80
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=80
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
+org.eclipse.jdt.core.formatter.tabulation.char=tab
+org.eclipse.jdt.core.formatter.tabulation.size=4
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
diff --git a/org.eclipse.egit.core/.settings/org.eclipse.jdt.ui.prefs b/org.eclipse.egit.core/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 0000000000..709a44074c
--- /dev/null
+++ b/org.eclipse.egit.core/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,9 @@
+#Wed May 09 00:20:24 CEST 2007
+eclipse.preferences.version=1
+formatter_profile=_JGit
+formatter_settings_version=10
+org.eclipse.jdt.ui.ignorelowercasenames=true
+org.eclipse.jdt.ui.importorder=java;javax;org;com;
+org.eclipse.jdt.ui.ondemandthreshold=99
+org.eclipse.jdt.ui.staticondemandthreshold=99
+org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8"?><templates/>
diff --git a/org.eclipse.egit.core/META-INF/MANIFEST.MF b/org.eclipse.egit.core/META-INF/MANIFEST.MF
new file mode 100644
index 0000000000..b290075f61
--- /dev/null
+++ b/org.eclipse.egit.core/META-INF/MANIFEST.MF
@@ -0,0 +1,21 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %plugin_name
+Bundle-SymbolicName: org.eclipse.egit.core;singleton:=true
+Bundle-Version: 0.5.0.qualifier
+Bundle-Activator: org.eclipse.egit.core.Activator
+Bundle-Vendor: %provider_name
+Bundle-Localization: plugin
+Require-Bundle: org.eclipse.core.runtime,
+ org.eclipse.team.core,
+ org.eclipse.core.resources,
+ org.eclipse.jgit,
+ org.eclipse.core.filesystem,
+ org.eclipse.ui
+Export-Package: org.eclipse.egit.core,
+ org.eclipse.egit.core.internal.storage;x-friends:="org.eclipse.egit.ui",
+ org.eclipse.egit.core.internal.util;x-friends:="org.eclipse.egit.ui",
+ org.eclipse.egit.core.op,
+ org.eclipse.egit.core.project
+Bundle-ActivationPolicy: lazy
+Bundle-RequiredExecutionEnvironment: J2SE-1.5
diff --git a/org.eclipse.egit.core/build.properties b/org.eclipse.egit.core/build.properties
new file mode 100644
index 0000000000..59c50b7cdf
--- /dev/null
+++ b/org.eclipse.egit.core/build.properties
@@ -0,0 +1,7 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ plugin.xml,\
+ .options,\
+ plugin.properties
diff --git a/org.eclipse.egit.core/plugin.properties b/org.eclipse.egit.core/plugin.properties
new file mode 100644
index 0000000000..9b61e82fea
--- /dev/null
+++ b/org.eclipse.egit.core/plugin.properties
@@ -0,0 +1,10 @@
+###############################################################################
+# Copyright (c) 2005, 2009 Shawn Pearce and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+###############################################################################
+plugin_name=Git Team Provider (Core) (Incubation)
+provider_name=eclipse.org
diff --git a/org.eclipse.egit.core/plugin.xml b/org.eclipse.egit.core/plugin.xml
new file mode 100644
index 0000000000..9b9b1f71aa
--- /dev/null
+++ b/org.eclipse.egit.core/plugin.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.0"?>
+<plugin>
+ <extension point="org.eclipse.core.runtime.preferences">
+ <initializer class="org.eclipse.egit.core.GitCorePreferenceInitializer"/>
+ </extension>
+
+ <extension point="org.eclipse.team.core.repository">
+ <repository
+ class="org.eclipse.egit.core.GitProvider"
+ id="org.eclipse.egit.core.GitProvider">
+ </repository>
+ </extension>
+ <extension
+ point="org.eclipse.team.core.ignore">
+ <ignore
+ enabled="true"
+ pattern=".git"/>
+ </extension>
+</plugin>
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/Activator.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/Activator.java
new file mode 100644
index 0000000000..86f38978f4
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/Activator.java
@@ -0,0 +1,102 @@
+/*******************************************************************************
+ * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.egit.core;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.egit.core.project.GitProjectData;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+/**
+ * The plugin class for the org.eclipse.egit.core plugin. This
+ * is a singleton class.
+ */
+public class Activator extends AbstractUIPlugin {
+ private static Activator plugin;
+
+ /**
+ * @return the singleton {@link Activator}
+ */
+ public static Activator getDefault() {
+ return plugin;
+ }
+
+ /**
+ * @return the name of this plugin
+ */
+ public static String getPluginId() {
+ return getDefault().getBundle().getSymbolicName();
+ }
+
+ /**
+ * Utility method to help throwing errors in the Egit plugin. This method
+ * does not actually throw the exception, but just creates an instance.
+ *
+ * @param message User comprehensible message
+ * @param thr cause
+ * @return an Initialized {@link CoreException}
+ */
+ public static CoreException error(final String message, final Throwable thr) {
+ return new CoreException(new Status(IStatus.ERROR, getPluginId(), 0,
+ message, thr));
+ }
+
+ /**
+ * Utility method to log errors in the Egit plugin.
+ * @param message User comprehensible message
+ * @param thr The exception through which we noticed the error
+ */
+ public static void logError(final String message, final Throwable thr) {
+ getDefault().getLog().log(
+ new Status(IStatus.ERROR, getPluginId(), 0, message, thr));
+ }
+
+ private static boolean isOptionSet(final String optionId) {
+ final String option = getPluginId() + optionId;
+ final String value = Platform.getDebugOption(option);
+ return value != null && value.equals("true");
+ }
+
+ /**
+ * Utility method for debug logging.
+ *
+ * @param what
+ */
+ public static void trace(final String what) {
+ if (getDefault().traceVerbose) {
+ System.out.println("[" + getPluginId() + "] " + what);
+ }
+ }
+
+ private boolean traceVerbose;
+
+ /**
+ * Construct the {@link Activator} singleton instance
+ */
+ public Activator() {
+ plugin = this;
+ }
+
+ public void start(final BundleContext context) throws Exception {
+ super.start(context);
+ traceVerbose = isOptionSet("/trace/verbose");
+ GitProjectData.reconfigureWindowCache();
+ GitProjectData.attachToWorkspace(true);
+ }
+
+ public void stop(final BundleContext context) throws Exception {
+ GitProjectData.detachFromWorkspace();
+ super.stop(context);
+ plugin = null;
+ }
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/AdaptableFileTreeIterator.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/AdaptableFileTreeIterator.java
new file mode 100644
index 0000000000..3fbab868a8
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/AdaptableFileTreeIterator.java
@@ -0,0 +1,88 @@
+/*******************************************************************************
+ * Copyright (C) 2009, Tor Arne Vestbø <torarnv@gmail.com>
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.eclipse.egit.core;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IWorkspaceRoot;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.treewalk.AbstractTreeIterator;
+import org.eclipse.jgit.treewalk.FileTreeIterator;
+
+/**
+ * Java IO file tree iterator that can adapt to a {@link ContainerTreeIterator}
+ * <p>
+ * The iterator automatically adapts to a {@link ContainerTreeIterator} when
+ * recursing into directories that are accessible from the given workspace root.
+ *
+ * @see org.eclipse.jgit.treewalk.FileTreeIterator
+ * @see org.eclipse.egit.core.ContainerTreeIterator
+ */
+public class AdaptableFileTreeIterator extends FileTreeIterator {
+
+ IWorkspaceRoot root;
+
+ /**
+ * Create a new iterator to traverse the given directory and its children
+ * <p>
+ * The iterator will automatically adapt to a {@link ContainerTreeIterator}
+ * when encountering directories what can be mapped into the given workspace
+ * root.
+ *
+ * @param path
+ * the starting directory. This directory should correspond to
+ * the repository root.
+ * @param workspaceRoot
+ * the workspace root to check resource mapping against.
+ *
+ */
+ public AdaptableFileTreeIterator(final File path,
+ final IWorkspaceRoot workspaceRoot) {
+ super(path);
+ root = workspaceRoot;
+ }
+
+ /**
+ * Create a new iterator to traverse a subdirectory.
+ * <p>
+ * The iterator will automatically adapt to a {@link ContainerTreeIterator}
+ * when encountering directories what can be mapped into the given workspace
+ * root.
+ *
+ * @param path
+ * the subdirectory. This should be a directory contained within
+ * the parent directory.
+ * @param parent
+ * the parent iterator we were created from.
+ * @param workspaceRoot
+ * the workspace root to check resource mapping against.
+ */
+ protected AdaptableFileTreeIterator(final AdaptableFileTreeIterator parent,
+ File path, final IWorkspaceRoot workspaceRoot) {
+ super(parent, path);
+ root = workspaceRoot;
+ }
+
+ @Override
+ public AbstractTreeIterator createSubtreeIterator(Repository repo)
+ throws IncorrectObjectTypeException, IOException {
+ final File currentFile = ((FileEntry) current()).getFile();
+ final IContainer[] containers = root.findContainersForLocation(new Path(
+ currentFile.getAbsolutePath()));
+ if (containers.length > 0)
+ return new ContainerTreeIterator(this, containers[0]);
+ else
+ return new AdaptableFileTreeIterator(this, currentFile, root);
+ }
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/ContainerTreeIterator.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/ContainerTreeIterator.java
new file mode 100644
index 0000000000..4bec94fbf5
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/ContainerTreeIterator.java
@@ -0,0 +1,239 @@
+/*******************************************************************************
+ * Copyright (C) 2008, Google Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.eclipse.egit.core;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspaceRoot;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.egit.core.project.RepositoryMapping;
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.treewalk.AbstractTreeIterator;
+import org.eclipse.jgit.treewalk.WorkingTreeIterator;
+import org.eclipse.jgit.util.FS;
+
+/**
+ * Adapts an Eclipse {@link IContainer} for use in a <code>TreeWalk</code>.
+ * <p>
+ * This iterator converts an Eclipse IContainer object into something that a
+ * TreeWalk instance can iterate over in parallel with any other Git tree data
+ * structure, such as another working directory tree from outside of the
+ * workspace or a stored tree from a Repository object database.
+ * <p>
+ * Modification times provided by this iterator are obtained from the cache
+ * Eclipse uses to track external resource modification. This can be faster, but
+ * requires the user refresh their workspace when external modifications take
+ * place. This is not really a concern as it is common practice to need to do a
+ * workspace refresh after externally modifying a file.
+ *
+ * @see org.eclipse.jgit.treewalk.TreeWalk
+ */
+public class ContainerTreeIterator extends WorkingTreeIterator {
+ private static String computePrefix(final IContainer base) {
+ final RepositoryMapping rm = RepositoryMapping.getMapping(base);
+ if (rm == null)
+ throw new IllegalArgumentException("Not in a Git project: " + base);
+ return rm.getRepoRelativePath(base);
+ }
+
+ private final IContainer node;
+
+ /**
+ * Construct a new iterator from a container in the workspace.
+ * <p>
+ * The iterator will support traversal over the named container, but only if
+ * it is contained within a project which has the Git repository provider
+ * connected and this resource is mapped into a Git repository. During the
+ * iteration the paths will be automatically generated to match the proper
+ * repository paths for this container's children.
+ *
+ * @param base
+ * the part of the workspace the iterator will walk over.
+ */
+ public ContainerTreeIterator(final IContainer base) {
+ super(computePrefix(base));
+ node = base;
+ init(entries());
+ }
+
+ /**
+ * Construct a new iterator from the workspace root.
+ * <p>
+ * The iterator will support traversal over workspace projects that have
+ * a Git repository provider connected and is mapped into a Git repository.
+ * During the iteration the paths will be automatically generated to match
+ * the proper repository paths for this container's children.
+ *
+ * @param root
+ * the workspace root to walk over.
+ */
+ public ContainerTreeIterator(final IWorkspaceRoot root) {
+ super("");
+ node = root;
+ init(entries());
+ }
+
+ /**
+ * Construct a new iterator from a container in the workspace, with a given
+ * parent iterator.
+ * <p>
+ * The iterator will support traversal over the named container, but only if
+ * it is contained within a project which has the Git repository provider
+ * connected and this resource is mapped into a Git repository. During the
+ * iteration the paths will be automatically generated to match the proper
+ * repository paths for this container's children.
+ *
+ * @param p
+ * the parent iterator we were created from.
+ * @param base
+ * the part of the workspace the iterator will walk over.
+ */
+ public ContainerTreeIterator(final WorkingTreeIterator p,
+ final IContainer base) {
+ super(p);
+ node = base;
+ init(entries());
+ }
+
+ @Override
+ public AbstractTreeIterator createSubtreeIterator(final Repository db)
+ throws IncorrectObjectTypeException, IOException {
+ if (FileMode.TREE.equals(mode))
+ return new ContainerTreeIterator(this,
+ (IContainer) ((ResourceEntry) current()).rsrc);
+ else
+ throw new IncorrectObjectTypeException(ObjectId.zeroId(),
+ Constants.TYPE_TREE);
+ }
+
+ /**
+ * Get the ResourceEntry for the current entry.
+ *
+ * @return the current entry
+ */
+ public ResourceEntry getResourceEntry() {
+ return (ResourceEntry) current();
+ }
+
+ private Entry[] entries() {
+ final IResource[] all;
+ try {
+ all = node.members(IContainer.INCLUDE_HIDDEN);
+ } catch (CoreException err) {
+ return EOF;
+ }
+
+ final Entry[] r = new Entry[all.length];
+ for (int i = 0; i < r.length; i++)
+ r[i] = new ResourceEntry(all[i]);
+ return r;
+ }
+
+ /**
+ * Wrapper for a resource in the Eclipse workspace
+ */
+ static public class ResourceEntry extends Entry {
+ final IResource rsrc;
+
+ private final FileMode mode;
+
+ private long length = -1;
+
+ ResourceEntry(final IResource f) {
+ rsrc = f;
+
+ switch (f.getType()) {
+ case IResource.FILE:
+ if (FS.INSTANCE.canExecute(asFile()))
+ mode = FileMode.EXECUTABLE_FILE;
+ else
+ mode = FileMode.REGULAR_FILE;
+ break;
+ case IResource.PROJECT:
+ case IResource.FOLDER: {
+ final IContainer c = (IContainer) f;
+ if (c.findMember(".git") != null)
+ mode = FileMode.GITLINK;
+ else
+ mode = FileMode.TREE;
+ break;
+ }
+ default:
+ mode = FileMode.MISSING;
+ break;
+ }
+ }
+
+ @Override
+ public FileMode getMode() {
+ return mode;
+ }
+
+ @Override
+ public String getName() {
+ if (rsrc.getType() == IResource.PROJECT)
+ return rsrc.getLocation().lastSegment();
+ else
+ return rsrc.getName();
+ }
+
+ @Override
+ public long getLength() {
+ if (length < 0) {
+ if (rsrc instanceof IFile)
+ length = asFile().length();
+ else
+ length = 0;
+ }
+ return length;
+ }
+
+ @Override
+ public long getLastModified() {
+ return rsrc.getLocalTimeStamp();
+ }
+
+ @Override
+ public InputStream openInputStream() throws IOException {
+ if (rsrc instanceof IFile) {
+ try {
+ return ((IFile) rsrc).getContents(true);
+ } catch (CoreException err) {
+ final IOException ioe = new IOException(err.getMessage());
+ ioe.initCause(err);
+ throw ioe;
+ }
+ }
+ throw new IOException("Not a regular file: " + rsrc);
+ }
+
+ /**
+ * Get the underlying resource of this entry.
+ *
+ * @return the underlying resource
+ */
+ public IResource getResource() {
+ return rsrc;
+ }
+
+ private File asFile() {
+ return ((IFile) rsrc).getLocation().toFile();
+ }
+ }
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/CoreText.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/CoreText.java
new file mode 100644
index 0000000000..6e82b9b573
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/CoreText.java
@@ -0,0 +1,106 @@
+/*******************************************************************************
+ * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.egit.core;
+
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * Possibly Translated strings for the Egit plugin.
+ */
+public class CoreText extends NLS {
+
+ /** */
+ public static String AssumeUnchangedOperation_adding;
+
+ /** */
+ public static String UpdateOperation_updating;
+
+ /** */
+ public static String UpdateOperation_failed;
+
+ /** */
+ public static String ConnectProviderOperation_connecting;
+
+ /** */
+ public static String ConnectProviderOperation_ConnectingProject;
+
+ /** */
+ public static String DisconnectProviderOperation_disconnecting;
+
+ /** */
+ public static String AddOperation_adding;
+
+ /** */
+ public static String AddOperation_failed;
+
+ /** */
+ public static String UntrackOperation_adding;
+
+ /** */
+ public static String UntrackOperation_failed;
+
+ /** */
+ public static String GitProjectData_lazyResolveFailed;
+
+ /** */
+ public static String GitProjectData_mappedResourceGone;
+
+ /** */
+ public static String GitProjectData_cannotReadHEAD;
+
+ /** */
+ public static String GitProjectData_missing;
+
+ /** */
+ public static String GitProjectData_saveFailed;
+
+ /** */
+ public static String GitProjectData_notifyChangedFailed;
+
+ /** */
+ public static String RepositoryFinder_finding;
+
+ /** */
+ public static String MoveDeleteHook_cannotModifyFolder;
+
+ /** */
+ public static String MoveDeleteHook_operationError;
+
+ /** */
+ public static String Error_CanonicalFile;
+
+ /** */
+ public static String CloneOperation_title;
+
+ /** */
+ public static String ListRemoteOperation_title;
+
+ /** */
+ public static String PushOperation_resultCancelled;
+
+ /** */
+ public static String PushOperation_resultNotSupported;
+
+ /** */
+ public static String PushOperation_resultTransportError;
+
+ /** */
+ public static String PushOperation_resultNoServiceError;
+
+ /** */
+ public static String PushOperation_taskNameDryRun;
+
+ /** */
+ public static String PushOperation_taskNameNormalRun;
+
+ static {
+ initializeMessages("org.eclipse.egit.core.coretext", CoreText.class);
+ }
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/EclipseGitProgressTransformer.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/EclipseGitProgressTransformer.java
new file mode 100644
index 0000000000..94f3edcca1
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/EclipseGitProgressTransformer.java
@@ -0,0 +1,110 @@
+/*******************************************************************************
+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.egit.core;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.SubProgressMonitor;
+import org.eclipse.jgit.lib.ProgressMonitor;
+
+/** Create a new Git to Eclipse progress monitor. */
+public class EclipseGitProgressTransformer implements ProgressMonitor {
+ private final IProgressMonitor root;
+
+ private IProgressMonitor task;
+
+ private String msg;
+
+ private int lastWorked;
+
+ private int totalWork;
+
+ /**
+ * Create a new progress monitor.
+ *
+ * @param eclipseMonitor
+ * the Eclipse monitor we update.
+ */
+ public EclipseGitProgressTransformer(final IProgressMonitor eclipseMonitor) {
+ root = eclipseMonitor;
+ }
+
+ public void start(final int totalTasks) {
+ root.beginTask("", totalTasks * 1000);
+ }
+
+ public void beginTask(final String name, final int total) {
+ endTask();
+ msg = name;
+ lastWorked = 0;
+ totalWork = total;
+ task = new SubProgressMonitor(root, 1000);
+ if (totalWork == UNKNOWN)
+ task.beginTask("", IProgressMonitor.UNKNOWN);
+ else
+ task.beginTask("", totalWork);
+ task.subTask(msg);
+ }
+
+ public void update(final int work) {
+ if (task == null)
+ return;
+
+ final int cmp = lastWorked + work;
+ if (lastWorked == UNKNOWN && cmp > 0) {
+ task.subTask(msg + ", " + cmp);
+ } else if (totalWork <= 0) {
+ // Do nothing to update the task.
+ } else if (cmp * 100 / totalWork != lastWorked * 100 / totalWork) {
+ final StringBuilder m = new StringBuilder();
+ m.append(msg);
+ m.append(": ");
+ while (m.length() < 25)
+ m.append(' ');
+
+ if (totalWork == UNKNOWN) {
+ m.append(cmp);
+ } else {
+ final String twstr = String.valueOf(totalWork);
+ String cmpstr = String.valueOf(cmp);
+ while (cmpstr.length() < twstr.length())
+ cmpstr = " " + cmpstr;
+ final int pcnt = (cmp * 100 / totalWork);
+ if (pcnt < 100)
+ m.append(' ');
+ if (pcnt < 10)
+ m.append(' ');
+ m.append(pcnt);
+ m.append("% (");
+ m.append(cmpstr);
+ m.append("/");
+ m.append(twstr);
+ m.append(")");
+ }
+ task.subTask(m.toString());
+ }
+ lastWorked = cmp;
+ task.worked(work);
+ }
+
+ public void endTask() {
+ if (task != null) {
+ try {
+ task.done();
+ } finally {
+ task = null;
+ }
+ }
+ }
+
+ public boolean isCancelled() {
+ if (task != null)
+ return task.isCanceled();
+ return root.isCanceled();
+ }
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/GitCorePreferenceInitializer.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/GitCorePreferenceInitializer.java
new file mode 100644
index 0000000000..6435a953e0
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/GitCorePreferenceInitializer.java
@@ -0,0 +1,27 @@
+/*******************************************************************************
+ * Copyright (C) 2008, Roger C. Soares <rogersoares@intelinet.com.br>
+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.egit.core;
+
+import org.eclipse.core.runtime.Preferences;
+import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer;
+
+/** Initializes plugin preferences with default values. */
+public class GitCorePreferenceInitializer extends AbstractPreferenceInitializer {
+ private static final int MB = 1024 * 1024;
+
+ public void initializeDefaultPreferences() {
+ final Preferences p = Activator.getDefault().getPluginPreferences();
+
+ p.setDefault(GitCorePreferences.core_packedGitWindowSize, 8 * 1024);
+ p.setDefault(GitCorePreferences.core_packedGitLimit, 10 * MB);
+ p.setDefault(GitCorePreferences.core_packedGitMMAP, false);
+ p.setDefault(GitCorePreferences.core_deltaBaseCacheLimit, 10 * MB);
+ }
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/GitCorePreferences.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/GitCorePreferences.java
new file mode 100644
index 0000000000..20238862ec
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/GitCorePreferences.java
@@ -0,0 +1,21 @@
+/*******************************************************************************
+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.egit.core;
+
+/** Preferences used by the core plugin. */
+public class GitCorePreferences {
+ /** */
+ public static final String core_packedGitWindowSize = "core_packedGitWindowSize";
+ /** */
+ public static final String core_packedGitLimit = "core_packedGitLimit";
+ /** */
+ public static final String core_packedGitMMAP = "core_packedGitMMAP";
+ /** */
+ public static final String core_deltaBaseCacheLimit = "core_deltaBaseCacheLimit";
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/GitMoveDeleteHook.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/GitMoveDeleteHook.java
new file mode 100644
index 0000000000..7fd82c9615
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/GitMoveDeleteHook.java
@@ -0,0 +1,205 @@
+/*******************************************************************************
+ * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2007, Shawn O. Pearce <spearce@spearce.org>
+ * Copyright (C) 2008, Google Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.egit.core;
+
+import java.io.IOException;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IProjectDescription;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.team.IMoveDeleteHook;
+import org.eclipse.core.resources.team.IResourceTree;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.egit.core.project.GitProjectData;
+import org.eclipse.egit.core.project.RepositoryMapping;
+import org.eclipse.jgit.dircache.DirCache;
+import org.eclipse.jgit.dircache.DirCacheBuilder;
+import org.eclipse.jgit.dircache.DirCacheEditor;
+import org.eclipse.jgit.dircache.DirCacheEntry;
+
+class GitMoveDeleteHook implements IMoveDeleteHook {
+ private static final boolean I_AM_DONE = true;
+
+ private static final boolean FINISH_FOR_ME = false;
+
+ private final GitProjectData data;
+
+ GitMoveDeleteHook(final GitProjectData d) {
+ Assert.isNotNull(d);
+ data = d;
+ }
+
+ public boolean deleteFile(final IResourceTree tree, final IFile file,
+ final int updateFlags, final IProgressMonitor monitor) {
+ final boolean force = (updateFlags & IResource.FORCE) == IResource.FORCE;
+ if (!force && !tree.isSynchronized(file, IResource.DEPTH_ZERO))
+ return false;
+
+ final RepositoryMapping map = RepositoryMapping.getMapping(file);
+ if (map == null)
+ return false;
+
+ try {
+ final DirCache dirc = DirCache.lock(map.getRepository());
+ final int first = dirc.findEntry(map.getRepoRelativePath(file));
+ if (first < 0) {
+ dirc.unlock();
+ return false;
+ }
+
+ final DirCacheBuilder edit = dirc.builder();
+ if (first > 0)
+ edit.keep(0, first);
+ final int next = dirc.nextEntry(first);
+ if (next < dirc.getEntryCount())
+ edit.keep(next, dirc.getEntryCount() - next);
+ if (!edit.commit())
+ tree.failed(new Status(IStatus.ERROR, Activator.getPluginId(),
+ 0, CoreText.MoveDeleteHook_operationError, null));
+ tree.standardDeleteFile(file, updateFlags, monitor);
+ } catch (IOException e) {
+ tree.failed(new Status(IStatus.ERROR, Activator.getPluginId(), 0,
+ CoreText.MoveDeleteHook_operationError, e));
+ }
+ return true;
+ }
+
+ public boolean deleteFolder(final IResourceTree tree, final IFolder folder,
+ final int updateFlags, final IProgressMonitor monitor) {
+ // Deleting a GIT repository which is in use is a pretty bad idea. To
+ // delete disconnect the team provider first.
+ //
+ if (data.isProtected(folder)) {
+ return cannotModifyRepository(tree);
+ } else {
+ return FINISH_FOR_ME;
+ }
+ }
+
+ public boolean deleteProject(final IResourceTree tree,
+ final IProject project, final int updateFlags,
+ final IProgressMonitor monitor) {
+ // TODO: Note that eclipse thinks folders are real, while
+ // Git does not care.
+ return FINISH_FOR_ME;
+ }
+
+ public boolean moveFile(final IResourceTree tree, final IFile srcf,
+ final IFile dstf, final int updateFlags,
+ final IProgressMonitor monitor) {
+ final boolean force = (updateFlags & IResource.FORCE) == IResource.FORCE;
+ if (!force && !tree.isSynchronized(srcf, IResource.DEPTH_ZERO))
+ return false;
+
+ final RepositoryMapping srcm = RepositoryMapping.getMapping(srcf);
+ if (srcm == null)
+ return false;
+ final RepositoryMapping dstm = RepositoryMapping.getMapping(dstf);
+
+ try {
+ final DirCache sCache = DirCache.lock(srcm.getRepository());
+ final String sPath = srcm.getRepoRelativePath(srcf);
+ final DirCacheEntry sEnt = sCache.getEntry(sPath);
+ if (sEnt == null) {
+ sCache.unlock();
+ return false;
+ }
+
+ final DirCacheEditor sEdit = sCache.editor();
+ sEdit.add(new DirCacheEditor.DeletePath(sEnt));
+ if (dstm != null && dstm.getRepository() == srcm.getRepository()) {
+ final String dPath = srcm.getRepoRelativePath(dstf);
+ sEdit.add(new DirCacheEditor.PathEdit(dPath) {
+ @Override
+ public void apply(final DirCacheEntry dEnt) {
+ dEnt.copyMetaData(sEnt);
+ }
+ });
+ }
+ if (!sEdit.commit())
+ tree.failed(new Status(IStatus.ERROR, Activator.getPluginId(),
+ 0, CoreText.MoveDeleteHook_operationError, null));
+
+ tree.standardMoveFile(srcf, dstf, updateFlags, monitor);
+ } catch (IOException e) {
+ tree.failed(new Status(IStatus.ERROR, Activator.getPluginId(), 0,
+ CoreText.MoveDeleteHook_operationError, e));
+ }
+ return true;
+ }
+
+ public boolean moveFolder(final IResourceTree tree, final IFolder srcf,
+ final IFolder dstf, final int updateFlags,
+ final IProgressMonitor monitor) {
+ final boolean force = (updateFlags & IResource.FORCE) == IResource.FORCE;
+ if (!force && !tree.isSynchronized(srcf, IResource.DEPTH_ZERO))
+ return false;
+
+ final RepositoryMapping srcm = RepositoryMapping.getMapping(srcf);
+ if (srcm == null)
+ return false;
+ final RepositoryMapping dstm = RepositoryMapping.getMapping(dstf);
+
+ try {
+ final DirCache sCache = DirCache.lock(srcm.getRepository());
+ final String sPath = srcm.getRepoRelativePath(srcf);
+ final DirCacheEntry[] sEnt = sCache.getEntriesWithin(sPath);
+ if (sEnt.length == 0) {
+ sCache.unlock();
+ return false;
+ }
+
+ final DirCacheEditor sEdit = sCache.editor();
+ sEdit.add(new DirCacheEditor.DeleteTree(sPath));
+ if (dstm != null && dstm.getRepository() == srcm.getRepository()) {
+ final String dPath = srcm.getRepoRelativePath(dstf) + "/";
+ final int sPathLen = sPath.length() + 1;
+ for (final DirCacheEntry se : sEnt) {
+ final String p = se.getPathString().substring(sPathLen);
+ sEdit.add(new DirCacheEditor.PathEdit(dPath + p) {
+ @Override
+ public void apply(final DirCacheEntry dEnt) {
+ dEnt.copyMetaData(se);
+ }
+ });
+ }
+ }
+ if (!sEdit.commit())
+ tree.failed(new Status(IStatus.ERROR, Activator.getPluginId(),
+ 0, CoreText.MoveDeleteHook_operationError, null));
+
+ tree.standardMoveFolder(srcf, dstf, updateFlags, monitor);
+ } catch (IOException e) {
+ tree.failed(new Status(IStatus.ERROR, Activator.getPluginId(), 0,
+ CoreText.MoveDeleteHook_operationError, e));
+ }
+ return true;
+ }
+
+ public boolean moveProject(final IResourceTree tree, final IProject source,
+ final IProjectDescription description, final int updateFlags,
+ final IProgressMonitor monitor) {
+ // TODO: We should be able to do this without too much effort when the
+ // projects belong to the same Git repository.
+ return FINISH_FOR_ME;
+ }
+
+ private boolean cannotModifyRepository(final IResourceTree tree) {
+ tree.failed(new Status(IStatus.ERROR, Activator.getPluginId(), 0,
+ CoreText.MoveDeleteHook_cannotModifyFolder, null));
+ return I_AM_DONE;
+ }
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/GitProvider.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/GitProvider.java
new file mode 100644
index 0000000000..d646c15f44
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/GitProvider.java
@@ -0,0 +1,90 @@
+/*******************************************************************************
+ * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.egit.core;
+
+import org.eclipse.core.resources.IResourceRuleFactory;
+import org.eclipse.core.resources.team.IMoveDeleteHook;
+import org.eclipse.core.resources.team.ResourceRuleFactory;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.egit.core.internal.storage.GitFileHistoryProvider;
+import org.eclipse.egit.core.project.GitProjectData;
+import org.eclipse.team.core.RepositoryProvider;
+import org.eclipse.team.core.history.IFileHistoryProvider;
+
+/**
+ * The Team provider class for a Git repository.
+ */
+public class GitProvider extends RepositoryProvider {
+ private GitProjectData data;
+
+ private GitMoveDeleteHook hook;
+
+ private GitFileHistoryProvider historyProvider;
+
+ private final IResourceRuleFactory resourceRuleFactory = new GitResourceRuleFactory();
+
+ public String getID() {
+ return getClass().getName();
+ }
+
+ public void configureProject() throws CoreException {
+ getData().markTeamPrivateResources();
+ }
+
+ public void deconfigure() throws CoreException {
+ GitProjectData.delete(getProject());
+ }
+
+ public boolean canHandleLinkedResources() {
+ return true;
+ }
+
+ @Override
+ public boolean canHandleLinkedResourceURI() {
+ return true;
+ }
+
+ public synchronized IMoveDeleteHook getMoveDeleteHook() {
+ if (hook == null) {
+ GitProjectData _data = getData();
+ if (_data != null)
+ hook = new GitMoveDeleteHook(_data);
+ }
+ return hook;
+ }
+
+ /**
+ * @return information about the mapping of an Eclipse project
+ * to a Git repository.
+ */
+ public synchronized GitProjectData getData() {
+ if (data == null) {
+ data = GitProjectData.get(getProject());
+ }
+ return data;
+ }
+
+ public synchronized IFileHistoryProvider getFileHistoryProvider() {
+ if (historyProvider == null) {
+ historyProvider = new GitFileHistoryProvider();
+ }
+ return historyProvider;
+ }
+
+ @Override
+ public IResourceRuleFactory getRuleFactory() {
+ return resourceRuleFactory;
+ }
+
+ private static class GitResourceRuleFactory extends ResourceRuleFactory {
+ // Use the default rule factory instead of the
+ // pessimistic one by default.
+ }
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/GitTag.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/GitTag.java
new file mode 100644
index 0000000000..42581a35a2
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/GitTag.java
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2007, Shawn O. Pearce <spearce@spearce.org>
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.egit.core;
+
+import org.eclipse.team.core.history.ITag;
+
+/**
+ * A representation of a Git tag in Eclipse.
+ */
+public class GitTag implements ITag {
+
+ private String name;
+
+ /**
+ * Construct a GitTag object with a given name.
+ *
+ * @param name the Git tag name
+ */
+ public GitTag(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/ResourceList.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/ResourceList.java
new file mode 100644
index 0000000000..c475a0c1d0
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/ResourceList.java
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ * Copyright (C) 2006, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.egit.core;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IAdaptable;
+
+/** A list of IResource, adaptable to the first item. */
+public class ResourceList implements IAdaptable {
+ private final IResource[] list;
+
+ /**
+ * Create a new list of resources.
+ *
+ * @param items
+ * the items to contain in this list.
+ */
+ public ResourceList(final IResource[] items) {
+ list = items;
+ }
+
+ /**
+ * Get the items stored in this list.
+ *
+ * @return the list provided to our constructor.
+ */
+ public IResource[] getItems() {
+ return list;
+ }
+
+ public Object getAdapter(final Class adapter) {
+ if (adapter == IResource.class && list != null && list.length > 0)
+ return list[0];
+ return null;
+ }
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/coretext.properties b/org.eclipse.egit.core/src/org/eclipse/egit/core/coretext.properties
new file mode 100644
index 0000000000..9b7ed82bad
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/coretext.properties
@@ -0,0 +1,51 @@
+###############################################################################
+# Copyright (c) 2006, 2009 Shawn Pearce and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+###############################################################################
+
+ConnectProviderOperation_connecting=Connecting Git team provider.
+ConnectProviderOperation_ConnectingProject=Connecting project {0}
+
+DisconnectProviderOperation_disconnecting=Disconnecting Git team provider.
+
+UpdateOperation_updating=Updating index
+UpdateOperation_failed=Failed to update index
+
+AddOperation_adding=Adding resources.
+AddOperation_failed=Failed to add resource.
+
+AssumeUnchangedOperation_adding=Marking resources unchanged
+UntrackOperation_adding=Untracking (removing) resources.
+UntrackOperation_failed=Failed to untrack resource.
+
+GitProjectData_mappedResourceGone=A mapped resource no longer exists in Eclipse.
+GitProjectData_cannotReadHEAD=Current tree for `HEAD` cannot be obtained.
+GitProjectData_missing=Git team provider configuration has gone missing.
+GitProjectData_saveFailed=Saving Git team provider data to {0} failed.
+GitProjectData_notifyChangedFailed=Resource change notification failed.
+GitProjectData_lazyResolveFailed=Unable to load a referenced tree.
+
+RepositoryFinder_finding=Searching for associated repositories.
+
+MoveDeleteHook_cannotModifyFolder=Folder contains an active Git repository.\n\
+The folder cannot be moved, renamed or deleted until the team provider is disconnected.
+
+MoveDeleteHook_operationError=Error updating cache during move/delete.\n\
+The resource cannot be moved, renamed or deleted due to an internal error.
+
+Error_CanonicalFile=Unable to determine a canonical file path.
+
+CloneOperation_title=Cloning from {0}
+
+ListRemoteOperation_title=Getting remote branches information
+
+PushOperation_resultCancelled=Operation was cancelled.
+PushOperation_resultNotSupported=Can't push to {0}
+PushOperation_resultTransportError=Transport error occured during push operation: {0}
+PushOperation_resultNoServiceError=Push service is not available: {0}
+PushOperation_taskNameDryRun=Trying pushing to remote repositories
+PushOperation_taskNameNormalRun=Pushing to remote repositories
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/UpdateJob.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/UpdateJob.java
new file mode 100644
index 0000000000..de07e118ba
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/UpdateJob.java
@@ -0,0 +1,164 @@
+/*******************************************************************************
+ * Copyright (C) 2007, Robin Rosenberg <me@lathund.dewire.com>
+ * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.egit.core.internal;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.IdentityHashMap;
+import java.util.Iterator;
+
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceProxy;
+import org.eclipse.core.resources.IResourceProxyVisitor;
+import org.eclipse.core.resources.IResourceVisitor;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.egit.core.Activator;
+import org.eclipse.egit.core.CoreText;
+import org.eclipse.egit.core.project.RepositoryMapping;
+import org.eclipse.jgit.errors.NotSupportedException;
+import org.eclipse.jgit.lib.GitIndex;
+import org.eclipse.jgit.lib.GitIndex.Entry;
+
+/**
+ * This job updates the index with the content of all specified
+ * and tracked resources. If a project is selected all tracked
+ * resources withing that container are updated.
+ */
+public class UpdateJob extends Job {
+
+ private final Collection rsrcList;
+
+ /**
+ * Construct an UpdateJob for the specified resources.
+ *
+ * @param rsrcList
+ */
+ public UpdateJob(Collection rsrcList) {
+ super("Update index");
+ this.rsrcList = rsrcList;
+ setPriority(Job.LONG);
+ }
+
+ protected IStatus run(IProgressMonitor m) {
+ if (m == null) {
+ m = new NullProgressMonitor();
+ }
+
+ trace("running");
+ try {
+ final IdentityHashMap<RepositoryMapping, Boolean> tomerge = new IdentityHashMap<RepositoryMapping, Boolean>();
+ try {
+ final int[] count=new int[1];
+ long t0=System.currentTimeMillis();
+ for (Object obj : rsrcList) {
+ obj = ((IAdaptable)obj).getAdapter(IResource.class);
+ if (obj instanceof IContainer) {
+ ((IContainer)obj).accept(new IResourceProxyVisitor() {
+ public boolean visit(IResourceProxy rp) throws CoreException {
+ if (rp.getType() == IResource.FILE) {
+ count[0]++;
+ }
+ return true;
+ }
+ }, IContainer.EXCLUDE_DERIVED);
+ } else if (obj instanceof IResource) {
+ count[0]++;
+ }
+ }
+ long t1=System.currentTimeMillis();
+ System.out.println("Counted "+count[0]+" items to update in "+(t1-t0)/1000.0+"s");
+ m.beginTask(CoreText.UpdateOperation_updating, count[0]);
+ final IProgressMonitor fm = m;
+ for (Object obj : rsrcList) {
+ if (obj instanceof IResource) {
+ final IResource r = (IResource)obj;
+ final RepositoryMapping rm = RepositoryMapping.getMapping(r);
+ final GitIndex index = rm.getRepository().getIndex();
+ tomerge.put(rm, Boolean.TRUE);
+ if (r instanceof IContainer) {
+ ((IContainer)r).accept(new IResourceVisitor() {
+ public boolean visit(IResource resource) throws CoreException {
+ try {
+ if (resource.getType() == IResource.FILE) {
+ String path = rm.getRepoRelativePath(resource);
+ Entry entry = index.getEntry(path);
+ if (entry != null) {
+ entry.update(new File(rm.getWorkDir(),path));
+ }
+ fm.worked(1);
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ throw Activator.error(CoreText.UpdateOperation_failed, e);
+ }
+ return true;
+ }
+ },IResource.DEPTH_INFINITE, IContainer.EXCLUDE_DERIVED);
+ } else {
+ String path = rm.getRepoRelativePath(r);
+ Entry entry = index.getEntry(path);
+ if (entry != null) {
+ entry.update(new File(rm.getWorkDir(),path));
+ }
+ m.worked(1);
+ }
+ }
+ }
+ for (RepositoryMapping rm : tomerge.keySet()) {
+ m.setTaskName("Writing index for "+rm.getRepository().getDirectory());
+ rm.getRepository().getIndex().write();
+ }
+ } catch (NotSupportedException e) {
+ return Activator.error(e.getMessage(),e).getStatus();
+ } catch (RuntimeException e) {
+ e.printStackTrace();
+ return Activator.error(CoreText.UpdateOperation_failed, e).getStatus();
+ } catch (IOException e) {
+ e.printStackTrace();
+ return Activator.error(CoreText.UpdateOperation_failed, e).getStatus();
+ } catch (CoreException e) {
+ e.printStackTrace();
+ return Activator.error(CoreText.UpdateOperation_failed, e).getStatus();
+ } finally {
+ try {
+ final Iterator i = tomerge.keySet().iterator();
+ while (i.hasNext()) {
+ final RepositoryMapping r = (RepositoryMapping) i.next();
+ r.getRepository().getIndex().read();
+ r.fireRepositoryChanged();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ m.done();
+ }
+ }
+ } finally {
+ trace("done");
+ m.done();
+ }
+
+ return Status.OK_STATUS;
+ }
+
+ private void trace(final String m) {
+ Activator.trace("(UpdateJob)"+m);
+ }
+
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/storage/BlobStorage.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/storage/BlobStorage.java
new file mode 100644
index 0000000000..05aa0edf63
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/storage/BlobStorage.java
@@ -0,0 +1,81 @@
+/*******************************************************************************
+ * Copyright (C) 2006, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.egit.core.internal.storage;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.eclipse.core.internal.resources.ResourceException;
+import org.eclipse.core.resources.IResourceStatus;
+import org.eclipse.core.resources.IStorage;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.lib.Repository;
+
+/** Accesses a blob from Git. */
+class BlobStorage implements IStorage {
+ private final Repository db;
+
+ private final String path;
+
+ private final ObjectId blobId;
+
+ BlobStorage(final Repository repository, final String fileName,
+ final ObjectId blob) {
+ db = repository;
+ path = fileName;
+ blobId = blob;
+ }
+
+ public InputStream getContents() throws CoreException {
+ try {
+ return open();
+ } catch (IOException e) {
+ throw new ResourceException(IResourceStatus.FAILED_READ_LOCAL,
+ getFullPath(), "IO error reading Git blob " + blobId + ".",
+ e);
+ }
+ }
+
+ private InputStream open() throws IOException, ResourceException,
+ IncorrectObjectTypeException {
+ final ObjectLoader reader = db.openBlob(blobId);
+ if (reader == null)
+ throw new ResourceException(IResourceStatus.FAILED_READ_LOCAL,
+ getFullPath(), "Git blob " + blobId + " not found.", null);
+ final byte[] data = reader.getBytes();
+ if (reader.getType() != Constants.OBJ_BLOB)
+ throw new IncorrectObjectTypeException(blobId, Constants.TYPE_BLOB);
+ return new ByteArrayInputStream(data);
+ }
+
+ public IPath getFullPath() {
+ return Path.fromPortableString(path);
+ }
+
+ public String getName() {
+ final int last = path.lastIndexOf('/');
+ return last >= 0 ? path.substring(last + 1) : path;
+ }
+
+ public boolean isReadOnly() {
+ return true;
+ }
+
+ public Object getAdapter(final Class adapter) {
+ return null;
+ }
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/storage/CommitFileRevision.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/storage/CommitFileRevision.java
new file mode 100644
index 0000000000..2771e889b2
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/storage/CommitFileRevision.java
@@ -0,0 +1,131 @@
+/*******************************************************************************
+ * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.egit.core.internal.storage;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Map;
+
+import org.eclipse.core.internal.resources.ResourceException;
+import org.eclipse.core.resources.IResourceStatus;
+import org.eclipse.core.resources.IStorage;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.egit.core.GitTag;
+import org.eclipse.team.core.history.IFileRevision;
+import org.eclipse.team.core.history.ITag;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.treewalk.TreeWalk;
+
+/**
+ * An {@link IFileRevision} for a version of a specified resource in the
+ * specified commit (revision).
+ */
+class CommitFileRevision extends GitFileRevision {
+ private final Repository db;
+
+ private final RevCommit commit;
+
+ private final PersonIdent author;
+
+ private final String path;
+
+ private ObjectId blobId;
+
+ CommitFileRevision(final Repository repo, final RevCommit rc,
+ final String fileName) {
+ this(repo, rc, fileName, null);
+ }
+
+ CommitFileRevision(final Repository repo, final RevCommit rc,
+ final String fileName, final ObjectId blob) {
+ super(fileName);
+ db = repo;
+ commit = rc;
+ author = rc.getAuthorIdent();
+ path = fileName;
+ blobId = blob;
+ }
+
+ String getGitPath() {
+ return path;
+ }
+
+ public IStorage getStorage(final IProgressMonitor monitor)
+ throws CoreException {
+ if (blobId == null)
+ blobId = locateBlobObjectId();
+ return new BlobStorage(db, path, blobId);
+ }
+
+ public long getTimestamp() {
+ return author != null ? author.getWhen().getTime() : 0;
+ }
+
+ public String getContentIdentifier() {
+ return commit.getId().name();
+ }
+
+ public String getAuthor() {
+ return author != null ? author.getName() : null;
+ }
+
+ public String getComment() {
+ return commit.getShortMessage();
+ }
+
+ public String toString() {
+ return commit.getId() + ":" + path;
+ }
+
+ public ITag[] getTags() {
+ final Collection<GitTag> ret = new ArrayList<GitTag>();
+ for (final Map.Entry<String, Ref> tag : db.getTags().entrySet()) {
+ final ObjectId ref = tag.getValue().getPeeledObjectId();
+ if (ref == null)
+ continue;
+ if (!AnyObjectId.equals(ref, commit))
+ continue;
+ ret.add(new GitTag(tag.getKey()));
+ }
+ return ret.toArray(new ITag[ret.size()]);
+ }
+
+ /**
+ * Get the commit that introduced this file revision.
+ *
+ * @return the commit we most recently noticed this file in.
+ */
+ public RevCommit getRevCommit() {
+ return commit;
+ }
+
+ private ObjectId locateBlobObjectId() throws CoreException {
+ try {
+ final TreeWalk w = TreeWalk.forPath(db, path, commit.getTree());
+ if (w == null)
+ throw new ResourceException(IResourceStatus.FAILED_READ_LOCAL,
+ Path.fromPortableString(path), "Path not in "
+ + commit.getId() + ".", null);
+ return w.getObjectId(0);
+ } catch (IOException e) {
+ throw new ResourceException(IResourceStatus.FAILED_READ_LOCAL, Path
+ .fromPortableString(path), "IO error looking up path in "
+ + commit.getId() + ".", e);
+ }
+ }
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/storage/GitFileHistory.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/storage/GitFileHistory.java
new file mode 100644
index 0000000000..377b6abcd1
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/storage/GitFileHistory.java
@@ -0,0 +1,187 @@
+/*******************************************************************************
+ * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.egit.core.internal.storage;
+
+import java.io.IOException;
+import java.util.Collections;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.egit.core.Activator;
+import org.eclipse.egit.core.project.RepositoryMapping;
+import org.eclipse.team.core.history.IFileHistoryProvider;
+import org.eclipse.team.core.history.IFileRevision;
+import org.eclipse.team.core.history.provider.FileHistory;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.treewalk.filter.AndTreeFilter;
+import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
+import org.eclipse.jgit.treewalk.filter.TreeFilter;
+
+/**
+ * A list of revisions for a specific resource according to some filtering
+ * criterion. Though git really does not do file tracking, this corresponds to
+ * listing all files with the same path.
+ */
+class GitFileHistory extends FileHistory implements IAdaptable {
+ private static final int SINGLE_REVISION = IFileHistoryProvider.SINGLE_REVISION;
+
+ private static final IFileRevision[] NO_REVISIONS = {};
+
+ private static final int BATCH_SIZE = 256;
+
+ private final IResource resource;
+
+ private String gitPath;
+
+ private final KidWalk walk;
+
+ private final IFileRevision[] revisions;
+
+ GitFileHistory(final IResource rsrc, final int flags,
+ final IProgressMonitor monitor) {
+ resource = rsrc;
+ walk = buildWalk();
+ revisions = buildRevisions(monitor, flags);
+ }
+
+ private KidWalk buildWalk() {
+ final RepositoryMapping rm = RepositoryMapping.getMapping(resource);
+ if (rm == null) {
+ Activator.logError("Git not attached to project "
+ + resource.getProject().getName() + ".", null);
+ return null;
+ }
+
+ final KidWalk w = new KidWalk(rm.getRepository());
+ gitPath = rm.getRepoRelativePath(resource);
+ w.setTreeFilter(AndTreeFilter.create(PathFilterGroup
+ .createFromStrings(Collections.singleton(gitPath)),
+ TreeFilter.ANY_DIFF));
+ return w;
+ }
+
+ private IFileRevision[] buildRevisions(final IProgressMonitor monitor,
+ final int flags) {
+ if (walk == null)
+ return NO_REVISIONS;
+
+ final Repository db = walk.getRepository();
+ final RevCommit root;
+ try {
+ final AnyObjectId headId = db.resolve(Constants.HEAD);
+ if (headId == null) {
+ Activator.logError("No HEAD revision available from Git"
+ + " for project " + resource.getProject().getName()
+ + ".", null);
+ return NO_REVISIONS;
+ }
+
+ root = walk.parseCommit(headId);
+ if ((flags & SINGLE_REVISION) == SINGLE_REVISION) {
+ // If all Eclipse wants is one revision it probably is
+ // for the editor "quick diff" feature. We can pass off
+ // just the repository HEAD, even though it may not be
+ // the revision that most recently modified the path.
+ //
+ final CommitFileRevision single;
+ single = new CommitFileRevision(db, root, gitPath);
+ return new IFileRevision[] { single };
+ }
+
+ walk.markStart(root);
+ } catch (IOException e) {
+ Activator.logError("Invalid HEAD revision for project "
+ + resource.getProject().getName() + ".", e);
+ return NO_REVISIONS;
+ }
+
+ final KidCommitList list = new KidCommitList();
+ list.source(walk);
+ try {
+ for (;;) {
+ final int oldsz = list.size();
+ list.fillTo(oldsz + BATCH_SIZE - 1);
+ if (oldsz == list.size())
+ break;
+ if (monitor != null && monitor.isCanceled())
+ break;
+ }
+ } catch (IOException e) {
+ Activator.logError("Error parsing history for "
+ + resource.getFullPath() + ".", e);
+ return NO_REVISIONS;
+ }
+
+ final IFileRevision[] r = new IFileRevision[list.size()];
+ for (int i = 0; i < r.length; i++)
+ r[i] = new CommitFileRevision(db, list.get(i), gitPath);
+ return r;
+ }
+
+ public IFileRevision[] getContributors(final IFileRevision ifr) {
+ if (!(ifr instanceof CommitFileRevision))
+ return NO_REVISIONS;
+
+ final CommitFileRevision rev = (CommitFileRevision) ifr;
+ final Repository db = walk.getRepository();
+ final String p = rev.getGitPath();
+ final RevCommit c = rev.getRevCommit();
+ final IFileRevision[] r = new IFileRevision[c.getParentCount()];
+ for (int i = 0; i < r.length; i++)
+ r[i] = new CommitFileRevision(db, c.getParent(i), p);
+ return r;
+ }
+
+ public IFileRevision[] getTargets(final IFileRevision ifr) {
+ if (!(ifr instanceof CommitFileRevision))
+ return NO_REVISIONS;
+
+ final CommitFileRevision rev = (CommitFileRevision) ifr;
+ final Repository db = walk.getRepository();
+ final String p = rev.getGitPath();
+ final RevCommit rc = rev.getRevCommit();
+ if (!(rc instanceof KidCommit))
+ return NO_REVISIONS;
+
+ final KidCommit c = (KidCommit) rc;
+ final IFileRevision[] r = new IFileRevision[c.children.length];
+ for (int i = 0; i < r.length; i++)
+ r[i] = new CommitFileRevision(db, c.children[i], p);
+ return r;
+ }
+
+ public IFileRevision getFileRevision(final String id) {
+ if (id == null || id.equals("") || GitFileRevision.WORKSPACE.equals(id))
+ return new WorkspaceFileRevision(resource);
+ if (GitFileRevision.INDEX.equals(id))
+ return new IndexFileRevision(walk.getRepository(), gitPath);
+
+ // Only return a revision if it was matched by this filtered history
+ for (IFileRevision r : revisions) {
+ if (r.getContentIdentifier().equals(id))
+ return r;
+ }
+ return null;
+ }
+
+ public IFileRevision[] getFileRevisions() {
+ final IFileRevision[] r = new IFileRevision[revisions.length];
+ System.arraycopy(revisions, 0, r, 0, r.length);
+ return r;
+ }
+
+ public Object getAdapter(Class adapter) {
+ return null;
+ }
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/storage/GitFileHistoryProvider.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/storage/GitFileHistoryProvider.java
new file mode 100644
index 0000000000..b85800d95a
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/storage/GitFileHistoryProvider.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.egit.core.internal.storage;
+
+import org.eclipse.core.filesystem.IFileStore;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.team.core.history.IFileHistory;
+import org.eclipse.team.core.history.IFileRevision;
+import org.eclipse.team.core.history.provider.FileHistoryProvider;
+
+/**
+ * A {@link FileHistoryProvider} for Git. This class has methods for retrieving
+ * specific versions of a tracked resource.
+ */
+public class GitFileHistoryProvider extends FileHistoryProvider {
+ public IFileHistory getFileHistoryFor(IResource resource, int flags,
+ IProgressMonitor monitor) {
+ return new GitFileHistory(resource, flags, monitor);
+ }
+
+ public IFileRevision getWorkspaceFileRevision(IResource resource) {
+ return new WorkspaceFileRevision(resource);
+ }
+
+ public IFileHistory getFileHistoryFor(IFileStore store, int flags,
+ IProgressMonitor monitor) {
+ // TODO: implement getFileHistoryFor(IFileStore ...)
+ return null;
+ }
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/storage/GitFileRevision.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/storage/GitFileRevision.java
new file mode 100644
index 0000000000..d9066573d9
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/storage/GitFileRevision.java
@@ -0,0 +1,91 @@
+/*******************************************************************************
+ * Copyright (C) 2006, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.egit.core.internal.storage;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.team.core.history.IFileRevision;
+import org.eclipse.team.core.history.provider.FileRevision;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+
+/**
+ * A Git related {@link IFileRevision}. It references a version and a resource,
+ * i.e. the version we think corresponds to the resource in specific version.
+ */
+public abstract class GitFileRevision extends FileRevision {
+ /** Content identifier for the working copy. */
+ public static final String WORKSPACE = "Workspace";
+
+ /** Content identifier for the content staged in the index. */
+ public static final String INDEX = "Index";
+
+ /**
+ * Obtain a file revision for a specific blob of an existing commit.
+ *
+ * @param db
+ * the repository this commit was loaded out of, and that this
+ * file's blob should also be reachable through.
+ * @param commit
+ * the commit the blob was identified to be within.
+ * @param path
+ * path within the commit's tree of the file.
+ * @param blobId
+ * unique name of the content.
+ * @return revision implementation for this file in the given commit.
+ */
+ public static GitFileRevision inCommit(final Repository db,
+ final RevCommit commit, final String path, final ObjectId blobId) {
+ return new CommitFileRevision(db, commit, path, blobId);
+ }
+
+ /**
+ * @param db
+ * the repository which contains the index to use.
+ * @param path
+ * path of the resource in the index
+ * @return revision implementation for the given path in the index
+ */
+ public static GitFileRevision inIndex(final Repository db, final String path) {
+ return new IndexFileRevision(db, path);
+ }
+
+ private final String path;
+
+ GitFileRevision(final String fileName) {
+ path = fileName;
+ }
+
+ public String getName() {
+ final int last = path.lastIndexOf('/');
+ return last >= 0 ? path.substring(last + 1) : path;
+ }
+
+ public boolean isPropertyMissing() {
+ return false;
+ }
+
+ public IFileRevision withAllProperties(final IProgressMonitor monitor)
+ throws CoreException {
+ return this;
+ }
+
+ public URI getURI() {
+ try {
+ return new URI(null, null, path, null);
+ } catch (URISyntaxException e) {
+ return null;
+ }
+ }
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/storage/IndexFileRevision.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/storage/IndexFileRevision.java
new file mode 100644
index 0000000000..8cf81c9ecb
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/storage/IndexFileRevision.java
@@ -0,0 +1,88 @@
+/*******************************************************************************
+ * Copyright (C) 2007, Robin Rosenberg <me@lathund.dewire.com>
+ * Copyright (C) 2006, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.egit.core.internal.storage;
+
+import java.io.IOException;
+
+import org.eclipse.core.internal.resources.ResourceException;
+import org.eclipse.core.resources.IResourceStatus;
+import org.eclipse.core.resources.IStorage;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.team.core.history.IFileRevision;
+import org.eclipse.jgit.lib.GitIndex;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.GitIndex.Entry;
+
+/** An {@link IFileRevision} for the version in the Git index. */
+class IndexFileRevision extends GitFileRevision implements IFileRevision {
+ private final Repository db;
+
+ private final String path;
+
+ private ObjectId blobId;
+
+ IndexFileRevision(final Repository repo, final String fileName) {
+ super(fileName);
+ db = repo;
+ path = fileName;
+ }
+
+ public IStorage getStorage(IProgressMonitor monitor) throws CoreException {
+ if (blobId == null)
+ blobId = locateBlobObjectId();
+ return new BlobStorage(db, path, blobId);
+ }
+
+ public boolean isPropertyMissing() {
+ return false;
+ }
+
+ public IFileRevision withAllProperties(IProgressMonitor monitor)
+ throws CoreException {
+ return null;
+ }
+
+ public String getAuthor() {
+ return "";
+ }
+
+ public long getTimestamp() {
+ return -1;
+ }
+
+ public String getComment() {
+ return null;
+ }
+
+ public String getContentIdentifier() {
+ return INDEX;
+ }
+
+ private ObjectId locateBlobObjectId() throws CoreException {
+ try {
+ final GitIndex idx = db.getIndex();
+ final Entry e = idx.getEntry(path);
+ if (e == null)
+ throw new ResourceException(IResourceStatus.FAILED_READ_LOCAL,
+ Path.fromPortableString(path),
+ "Git index entry not found", null);
+ return e.getObjectId();
+
+ } catch (IOException e) {
+ throw new ResourceException(IResourceStatus.FAILED_READ_LOCAL, Path
+ .fromPortableString(path),
+ "IO error looking up path in index.", e);
+ }
+ }
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/storage/KidCommit.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/storage/KidCommit.java
new file mode 100644
index 0000000000..b94faeac12
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/storage/KidCommit.java
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.egit.core.internal.storage;
+
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.revwalk.RevCommit;
+
+class KidCommit extends RevCommit {
+ static final KidCommit[] NO_CHILDREN = {};
+
+ KidCommit[] children = NO_CHILDREN;
+
+ KidCommit(final AnyObjectId id) {
+ super(id);
+ }
+
+ void addChild(final KidCommit c) {
+ final int cnt = children.length;
+ if (cnt == 0)
+ children = new KidCommit[] { c };
+ else if (cnt == 1)
+ children = new KidCommit[] { children[0], c };
+ else {
+ final KidCommit[] n = new KidCommit[cnt + 1];
+ System.arraycopy(children, 0, n, 0, cnt);
+ n[cnt] = c;
+ children = n;
+ }
+ }
+
+ @Override
+ public void reset() {
+ children = NO_CHILDREN;
+ super.reset();
+ }
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/storage/KidCommitList.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/storage/KidCommitList.java
new file mode 100644
index 0000000000..4bc9fe16c9
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/storage/KidCommitList.java
@@ -0,0 +1,21 @@
+/*******************************************************************************
+ * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.egit.core.internal.storage;
+
+import org.eclipse.jgit.revwalk.RevCommitList;
+
+class KidCommitList extends RevCommitList<KidCommit> {
+ @Override
+ protected void enter(final int index, final KidCommit e) {
+ final int nParents = e.getParentCount();
+ for (int i = 0; i < nParents; i++)
+ ((KidCommit) e.getParent(i)).addChild(e);
+ }
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/storage/KidWalk.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/storage/KidWalk.java
new file mode 100644
index 0000000000..9865e377ad
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/storage/KidWalk.java
@@ -0,0 +1,26 @@
+/*******************************************************************************
+ * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.egit.core.internal.storage;
+
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
+
+class KidWalk extends RevWalk {
+ KidWalk(final Repository repo) {
+ super(repo);
+ }
+
+ @Override
+ protected RevCommit createCommit(final AnyObjectId id) {
+ return new KidCommit(id);
+ }
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/storage/WorkspaceFileRevision.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/storage/WorkspaceFileRevision.java
new file mode 100644
index 0000000000..e3f4500106
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/storage/WorkspaceFileRevision.java
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * Copyright (C) 2007, Robin Rosenberg <me@lathund.dewire.com>
+ * Copyright (C) 2006, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.egit.core.internal.storage;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IStorage;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.team.core.history.IFileRevision;
+
+/** An {@link IFileRevision} for the current version in the workspace. */
+class WorkspaceFileRevision extends GitFileRevision implements IFileRevision {
+ private final IResource rsrc;
+
+ WorkspaceFileRevision(final IResource resource) {
+ super(resource.getName());
+ rsrc = resource;
+ }
+
+ public IStorage getStorage(IProgressMonitor monitor) throws CoreException {
+ return rsrc instanceof IStorage ? (IStorage) rsrc : null;
+ }
+
+ public boolean isPropertyMissing() {
+ return false;
+ }
+
+ public IFileRevision withAllProperties(IProgressMonitor monitor)
+ throws CoreException {
+ return null;
+ }
+
+ public String getAuthor() {
+ return "";
+ }
+
+ public long getTimestamp() {
+ return -1;
+ }
+
+ public String getComment() {
+ return "";
+ }
+
+ public String getContentIdentifier() {
+ return WORKSPACE;
+ }
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/util/ExceptionCollector.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/util/ExceptionCollector.java
new file mode 100644
index 0000000000..a2cb287f42
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/util/ExceptionCollector.java
@@ -0,0 +1,128 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.egit.core.internal.util;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.ILog;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.MultiStatus;
+import org.eclipse.core.runtime.Status;
+
+/**
+ * Collects exceptions and can be configured to ignore duplicates exceptions.
+ * Exceptions can be logged and a MultiStatus containing all collected
+ * exceptions can be returned.
+ *
+ * @see org.eclipse.core.runtime.MultiStatus
+ * @see org.eclipse.core.runtime.IStatus
+ *
+ * @since 3.0
+ */
+public class ExceptionCollector {
+
+ private final List<IStatus> statuses = new ArrayList<IStatus>();
+
+ private final String message;
+
+ private final String pluginId;
+
+ private final int severity;
+
+ private final ILog log;
+
+ /**
+ * Creates a collector and initializes the parameters for the top-level
+ * exception that would be returned from <code>getStatus</code> is
+ * exceptions are collected.
+ *
+ * @param message
+ * a human-readable message, localized to the current locale
+ * @param pluginId
+ * the unique identifier of the relevant plug-in
+ * @param severity
+ * the severity; one of <code>OK</code>, <code>ERROR</code>,
+ * <code>INFO</code>, or <code>WARNING</code>
+ * @param log
+ * the log to output the exceptions to, or <code>null</code> if
+ * exceptions should not be logged.
+ */
+ public ExceptionCollector(String message, String pluginId, int severity,
+ ILog log) {
+ this.message = message;
+ this.pluginId = pluginId;
+ this.severity = severity;
+ this.log = log;
+ }
+
+ /**
+ * Clears the exceptions collected.
+ */
+ public void clear() {
+ statuses.clear();
+ }
+
+ /**
+ * Returns a status that represents the exceptions collected. If the
+ * collector is empty <code>IStatus.OK</code> is returned. Otherwise a
+ * MultiStatus containing all collected exceptions is returned.
+ *
+ * @return a multistatus containing the exceptions collected or IStatus.OK
+ * if the collector is empty.
+ */
+ public IStatus getStatus() {
+ if (statuses.isEmpty()) {
+ return Status.OK_STATUS;
+ } else {
+ final MultiStatus multiStatus = new MultiStatus(pluginId, severity,
+ message, null);
+ final Iterator it = statuses.iterator();
+ while (it.hasNext()) {
+ final IStatus status = (IStatus) it.next();
+ multiStatus.merge(status);
+ }
+ return multiStatus;
+ }
+ }
+
+ /**
+ * Add this exception to the collector. If a log was specified in the
+ * constructor then the exception will be output to the log. You can
+ * retreive exceptions using <code>getStatus</code>.
+ *
+ * @param exception
+ * the exception to collect
+ */
+ public void handleException(CoreException exception) {
+ if (log != null) {
+ log.log(new Status(severity, pluginId, 0, message, exception));
+ }
+
+ // Record each status individually to flatten the resulting multi-status
+ final IStatus exceptionStatus = exception.getStatus();
+
+ // Wrap the exception so the stack trace is not lost.
+ final IStatus status = new Status(exceptionStatus.getSeverity(),
+ exceptionStatus.getPlugin(), exceptionStatus.getCode(),
+ exceptionStatus.getMessage(), exception);
+
+ recordStatus(status);
+ for (IStatus childStatus : status.getChildren())
+ recordStatus(childStatus);
+ }
+
+ private void recordStatus(IStatus status) {
+ statuses.add(status);
+ }
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/op/AssumeUnchangedOperation.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/op/AssumeUnchangedOperation.java
new file mode 100644
index 0000000000..21d6d9dc36
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/op/AssumeUnchangedOperation.java
@@ -0,0 +1,125 @@
+/*******************************************************************************
+ * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
+ * Copyright (C) 2008, Google Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.egit.core.op;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.IdentityHashMap;
+import java.util.Map;
+
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspaceRunnable;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.egit.core.Activator;
+import org.eclipse.egit.core.CoreText;
+import org.eclipse.egit.core.project.GitProjectData;
+import org.eclipse.egit.core.project.RepositoryMapping;
+import org.eclipse.jgit.dircache.DirCache;
+import org.eclipse.jgit.dircache.DirCacheEntry;
+import org.eclipse.jgit.lib.Repository;
+
+/**
+ * Tell JGit to ignore changes in selected files
+ */
+public class AssumeUnchangedOperation implements IWorkspaceRunnable {
+ private final Collection rsrcList;
+
+ private final IdentityHashMap<Repository, DirCache> caches;
+
+ private final IdentityHashMap<RepositoryMapping, Object> mappings;
+
+ /**
+ * Create a new operation to ignore changes in tracked files
+ *
+ * @param rsrcs
+ * collection of {@link IResource}s which should be ignored when
+ * looking for changes or committing.
+ */
+ public AssumeUnchangedOperation(final Collection rsrcs) {
+ rsrcList = rsrcs;
+ caches = new IdentityHashMap<Repository, DirCache>();
+ mappings = new IdentityHashMap<RepositoryMapping, Object>();
+ }
+
+ public void run(IProgressMonitor m) throws CoreException {
+ if (m == null)
+ m = new NullProgressMonitor();
+
+ caches.clear();
+ mappings.clear();
+
+ m.beginTask(CoreText.AssumeUnchangedOperation_adding,
+ rsrcList.size() * 200);
+ try {
+ for (Object obj : rsrcList) {
+ obj = ((IAdaptable) obj).getAdapter(IResource.class);
+ if (obj instanceof IResource)
+ assumeValid((IResource) obj);
+ m.worked(200);
+ }
+
+ for (Map.Entry<Repository, DirCache> e : caches.entrySet()) {
+ final Repository db = e.getKey();
+ final DirCache editor = e.getValue();
+ m.setTaskName("Writing index for " + db.getDirectory());
+ editor.write();
+ editor.commit();
+ }
+ } catch (RuntimeException e) {
+ throw Activator.error(CoreText.UntrackOperation_failed, e);
+ } catch (IOException e) {
+ throw Activator.error(CoreText.UntrackOperation_failed, e);
+ } finally {
+ for (final RepositoryMapping rm : mappings.keySet())
+ rm.fireRepositoryChanged();
+ caches.clear();
+ mappings.clear();
+ m.done();
+ }
+ }
+
+ private void assumeValid(final IResource resource) throws CoreException {
+ final IProject proj = resource.getProject();
+ final GitProjectData pd = GitProjectData.get(proj);
+ if (pd == null)
+ return;
+ final RepositoryMapping rm = pd.getRepositoryMapping(resource);
+ if (rm == null)
+ return;
+ final Repository db = rm.getRepository();
+
+ DirCache cache = caches.get(db);
+ if (cache == null) {
+ try {
+ cache = DirCache.lock(db);
+ } catch (IOException err) {
+ throw Activator.error(CoreText.UntrackOperation_failed, err);
+ }
+ caches.put(db, cache);
+ mappings.put(rm, rm);
+ }
+
+ final String path = rm.getRepoRelativePath(resource);
+ if (resource instanceof IContainer) {
+ for (final DirCacheEntry ent : cache.getEntriesWithin(path))
+ ent.setAssumeValid(true);
+ } else {
+ final DirCacheEntry ent = cache.getEntry(path);
+ if (ent != null)
+ ent.setAssumeValid(true);
+ }
+ }
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/op/BranchOperation.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/op/BranchOperation.java
new file mode 100644
index 0000000000..7c50dff319
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/op/BranchOperation.java
@@ -0,0 +1,168 @@
+/*******************************************************************************
+ * Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
+ * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2006, Shawn O. Pearce <spearce@spearce.org>
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.egit.core.op;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspaceRunnable;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.jgit.errors.CheckoutConflictException;
+import org.eclipse.jgit.lib.Commit;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.GitIndex;
+import org.eclipse.jgit.lib.RefLogWriter;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.Tree;
+import org.eclipse.jgit.lib.WorkDirCheckout;
+
+/**
+ * This class implements checkouts of a specific revision. A check
+ * is made that this can be done without data loss.
+ */
+public class BranchOperation implements IWorkspaceRunnable {
+
+ private final Repository repository;
+
+ private final String refName;
+
+ /**
+ * Construct a {@link BranchOperation} object.
+ * @param repository
+ * @param refName Name of git ref to checkout
+ */
+ public BranchOperation(Repository repository, String refName) {
+ this.repository = repository;
+ this.refName = refName;
+ }
+
+ private Tree oldTree;
+
+ private GitIndex index;
+
+ private Tree newTree;
+
+ private Commit oldCommit;
+
+ private Commit newCommit;
+
+
+
+ public void run(IProgressMonitor monitor) throws CoreException {
+ lookupRefs();
+ monitor.worked(1);
+
+ mapObjects();
+ monitor.worked(1);
+
+ checkoutTree();
+ monitor.worked(1);
+
+ writeIndex();
+ monitor.worked(1);
+
+ updateHeadRef();
+ monitor.worked(1);
+
+ writeHeadReflog();
+ monitor.worked(1);
+
+ refreshProjects();
+ monitor.worked(1);
+
+ monitor.done();
+ }
+
+ private void refreshProjects() {
+ final IProject[] projects = ResourcesPlugin.getWorkspace().getRoot()
+ .getProjects();
+ final File parentFile = repository.getWorkDir();
+ for (IProject p : projects) {
+ final File file = p.getLocation().toFile();
+ if (file.getAbsolutePath().startsWith(parentFile.getAbsolutePath())) {
+ try {
+ System.out.println("Refreshing " + p);
+ p.refreshLocal(IResource.DEPTH_INFINITE, null);
+ } catch (CoreException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ private void writeHeadReflog() throws TeamException {
+ try {
+ RefLogWriter.writeReflog(repository, oldCommit.getCommitId(),
+ newCommit.getCommitId(), "checkout: moving to " + refName,
+ Constants.HEAD);
+ } catch (IOException e) {
+ throw new TeamException("Writing HEAD's reflog", e);
+ }
+ }
+
+ private void updateHeadRef() throws TeamException {
+ try {
+ repository.writeSymref(Constants.HEAD, refName);
+ } catch (IOException e) {
+ throw new TeamException("Updating HEAD to ref: " + refName, e);
+ }
+ }
+
+ private void writeIndex() throws TeamException {
+ try {
+ index.write();
+ } catch (IOException e) {
+ throw new TeamException("Writing index", e);
+ }
+ }
+
+ private void checkoutTree() throws TeamException {
+ try {
+ new WorkDirCheckout(repository, repository.getWorkDir(), oldTree,
+ index, newTree).checkout();
+ } catch (CheckoutConflictException e) {
+ TeamException teamException = new TeamException(e.getMessage());
+ throw teamException;
+ } catch (IOException e) {
+ throw new TeamException("Problem while checking out:", e);
+ }
+ }
+
+ private void mapObjects() throws TeamException {
+ try {
+ oldTree = oldCommit.getTree();
+ index = repository.getIndex();
+ newTree = newCommit.getTree();
+ } catch (IOException e) {
+ throw new TeamException("Mapping trees", e);
+ }
+ }
+
+ private void lookupRefs() throws TeamException {
+ try {
+ newCommit = repository.mapCommit(refName);
+ } catch (IOException e) {
+ throw new TeamException("Mapping commit: " + refName, e);
+ }
+
+ try {
+ oldCommit = repository.mapCommit(Constants.HEAD);
+ } catch (IOException e) {
+ throw new TeamException("Mapping commit HEAD commit", e);
+ }
+ }
+
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/op/CloneOperation.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/op/CloneOperation.java
new file mode 100644
index 0000000000..57ea79b0e6
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/op/CloneOperation.java
@@ -0,0 +1,221 @@
+/*******************************************************************************
+ * Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
+ * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2008, Roger C. Soares <rogersoares@intelinet.com.br>
+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
+ * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.egit.core.op;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.net.URISyntaxException;
+import java.util.Collection;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.SubProgressMonitor;
+import org.eclipse.egit.core.CoreText;
+import org.eclipse.egit.core.EclipseGitProgressTransformer;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.jgit.errors.NotSupportedException;
+import org.eclipse.jgit.errors.TransportException;
+import org.eclipse.jgit.lib.Commit;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.GitIndex;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.RepositoryConfig;
+import org.eclipse.jgit.lib.Tree;
+import org.eclipse.jgit.lib.WorkDirCheckout;
+import org.eclipse.jgit.transport.FetchResult;
+import org.eclipse.jgit.transport.RefSpec;
+import org.eclipse.jgit.transport.RemoteConfig;
+import org.eclipse.jgit.transport.Transport;
+import org.eclipse.jgit.transport.URIish;
+
+/**
+ * Clones a repository from a remote location to a local location.
+ */
+public class CloneOperation implements IRunnableWithProgress {
+ private final URIish uri;
+
+ private final boolean allSelected;
+
+ private final Collection<Ref> selectedBranches;
+
+ private final File workdir;
+
+ private final String branch;
+
+ private final String remoteName;
+
+ private Repository local;
+
+ private RemoteConfig remoteConfig;
+
+ private FetchResult fetchResult;
+
+ /**
+ * Create a new clone operation.
+ *
+ * @param uri
+ * remote we should fetch from.
+ * @param allSelected
+ * true when all branches have to be fetched (indicates wildcard
+ * in created fetch refspec), false otherwise.
+ * @param selectedBranches
+ * collection of branches to fetch. Ignored when allSelected is
+ * true.
+ * @param workdir
+ * working directory to clone to. The directory may or may not
+ * already exist.
+ * @param branch
+ * branch to initially clone from.
+ * @param remoteName
+ * name of created remote config as source remote (typically
+ * named "origin").
+ */
+ public CloneOperation(final URIish uri, final boolean allSelected,
+ final Collection<Ref> selectedBranches, final File workdir,
+ final String branch, final String remoteName) {
+ this.uri = uri;
+ this.allSelected = allSelected;
+ this.selectedBranches = selectedBranches;
+ this.workdir = workdir;
+ this.branch = branch;
+ this.remoteName = remoteName;
+ }
+
+ public void run(final IProgressMonitor pm)
+ throws InvocationTargetException, InterruptedException {
+ final IProgressMonitor monitor;
+ if (pm == null)
+ monitor = new NullProgressMonitor();
+ else
+ monitor = pm;
+
+ try {
+ monitor.beginTask(NLS.bind(CoreText.CloneOperation_title, uri),
+ 5000);
+ try {
+ doInit(new SubProgressMonitor(monitor, 100));
+ doFetch(new SubProgressMonitor(monitor, 4000));
+ doCheckout(new SubProgressMonitor(monitor, 900));
+ } finally {
+ closeLocal();
+ }
+ } catch (final Exception e) {
+ delete(workdir);
+ if (monitor.isCanceled())
+ throw new InterruptedException();
+ else
+ throw new InvocationTargetException(e);
+ } finally {
+ monitor.done();
+ }
+ }
+
+ private void closeLocal() {
+ if (local != null) {
+ local.close();
+ local = null;
+ }
+ }
+
+ private void doInit(final IProgressMonitor monitor)
+ throws URISyntaxException, IOException {
+ monitor.setTaskName("Initializing local repository");
+
+ final File gitdir = new File(workdir, ".git");
+ local = new Repository(gitdir);
+ local.create();
+ local.writeSymref(Constants.HEAD, branch);
+
+ remoteConfig = new RemoteConfig(local.getConfig(), remoteName);
+ remoteConfig.addURI(uri);
+
+ final String dst = Constants.R_REMOTES + remoteConfig.getName();
+ RefSpec wcrs = new RefSpec();
+ wcrs = wcrs.setForceUpdate(true);
+ wcrs = wcrs.setSourceDestination(Constants.R_HEADS + "*", dst + "/*");
+
+ if (allSelected) {
+ remoteConfig.addFetchRefSpec(wcrs);
+ } else {
+ for (final Ref ref : selectedBranches)
+ if (wcrs.matchSource(ref))
+ remoteConfig.addFetchRefSpec(wcrs.expandFromSource(ref));
+ }
+
+ // we're setting up for a clone with a checkout
+ local.getConfig().setBoolean("core", null, "bare", false);
+
+ remoteConfig.update(local.getConfig());
+
+ // branch is like 'Constants.R_HEADS + branchName', we need only
+ // the 'branchName' part
+ String branchName = branch.substring(Constants.R_HEADS.length());
+
+ // setup the default remote branch for branchName
+ local.getConfig().setString(RepositoryConfig.BRANCH_SECTION,
+ branchName, "remote", remoteName);
+ local.getConfig().setString(RepositoryConfig.BRANCH_SECTION,
+ branchName, "merge", branch);
+
+ local.getConfig().save();
+ }
+
+ private void doFetch(final IProgressMonitor monitor)
+ throws NotSupportedException, TransportException {
+ final Transport tn = Transport.open(local, remoteConfig);
+ try {
+ final EclipseGitProgressTransformer pm;
+ pm = new EclipseGitProgressTransformer(monitor);
+ fetchResult = tn.fetch(pm, null);
+ } finally {
+ tn.close();
+ }
+ }
+
+ private void doCheckout(final IProgressMonitor monitor) throws IOException {
+ final Ref head = fetchResult.getAdvertisedRef(branch);
+ if (head == null || head.getObjectId() == null)
+ return;
+
+ final GitIndex index = new GitIndex(local);
+ final Commit mapCommit = local.mapCommit(head.getObjectId());
+ final Tree tree = mapCommit.getTree();
+ final RefUpdate u;
+ final WorkDirCheckout co;
+
+ u = local.updateRef(Constants.HEAD);
+ u.setNewObjectId(mapCommit.getCommitId());
+ u.forceUpdate();
+
+ monitor.setTaskName("Checking out files");
+ co = new WorkDirCheckout(local, local.getWorkDir(), index, tree);
+ co.checkout();
+ monitor.setTaskName("Writing index");
+ index.write();
+ }
+
+ private static void delete(final File d) {
+ if (d.isDirectory()) {
+ final File[] items = d.listFiles();
+ if (items != null) {
+ for (final File c : items)
+ delete(c);
+ }
+ }
+ d.delete();
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/op/ConnectProviderOperation.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/op/ConnectProviderOperation.java
new file mode 100644
index 0000000000..a854418418
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/op/ConnectProviderOperation.java
@@ -0,0 +1,101 @@
+/*******************************************************************************
+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
+ * Copyright (C) 2008, Google Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.egit.core.op;
+
+import java.util.Collection;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspaceRunnable;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.SubProgressMonitor;
+import org.eclipse.egit.core.Activator;
+import org.eclipse.egit.core.CoreText;
+import org.eclipse.egit.core.GitProvider;
+import org.eclipse.egit.core.project.GitProjectData;
+import org.eclipse.egit.core.project.RepositoryFinder;
+import org.eclipse.egit.core.project.RepositoryMapping;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.team.core.RepositoryProvider;
+
+/**
+ * Connects Eclipse to an existing Git repository
+ */
+public class ConnectProviderOperation implements IWorkspaceRunnable {
+ private final IProject[] projects;
+
+ /**
+ * Create a new connection operation to execute within the workspace.
+ *
+ * @param proj
+ * the project to connect to the Git team provider.
+ */
+ public ConnectProviderOperation(final IProject proj) {
+ this(new IProject[] { proj });
+ }
+
+ /**
+ * Create a new connection operation to execute within the workspace.
+ *
+ * @param projects
+ * the projects to connect to the Git team provider.
+ */
+ public ConnectProviderOperation(final IProject[] projects) {
+ this.projects = projects;
+ }
+
+ public void run(IProgressMonitor m) throws CoreException {
+ if (m == null) {
+ m = new NullProgressMonitor();
+ }
+
+ m.beginTask(CoreText.ConnectProviderOperation_connecting,
+ 100 * projects.length);
+ try {
+
+ for (IProject project : projects) {
+ m.setTaskName(NLS.bind(
+ CoreText.ConnectProviderOperation_ConnectingProject,
+ project.getName()));
+ Activator.trace("Locating repository for " + project); //$NON-NLS-1$
+ Collection<RepositoryMapping> repos = new RepositoryFinder(
+ project).find(new SubProgressMonitor(m, 40));
+ if (repos.size() == 1) {
+ GitProjectData projectData = new GitProjectData(project);
+ try {
+ projectData.setRepositoryMappings(repos);
+ projectData.store();
+ } catch (CoreException ce) {
+ GitProjectData.delete(project);
+ throw ce;
+ } catch (RuntimeException ce) {
+ GitProjectData.delete(project);
+ throw ce;
+ }
+ RepositoryProvider
+ .map(project, GitProvider.class.getName());
+ projectData = GitProjectData.get(project);
+ project.refreshLocal(IResource.DEPTH_INFINITE,
+ new SubProgressMonitor(m, 50));
+ m.worked(10);
+ } else {
+ Activator
+ .trace("Attempted to share project without repository ignored :" //$NON-NLS-1$
+ + project);
+ m.worked(60);
+ }
+ }
+ } finally {
+ m.done();
+ }
+ }
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/op/DisconnectProviderOperation.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/op/DisconnectProviderOperation.java
new file mode 100644
index 0000000000..6f2b9ee07a
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/op/DisconnectProviderOperation.java
@@ -0,0 +1,91 @@
+/*******************************************************************************
+ * Copyright (C) 2007, Shawn O. Pearce <spearce@spearce.org>
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.egit.core.op;
+
+import java.util.Collection;
+
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspaceRunnable;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.SubProgressMonitor;
+import org.eclipse.egit.core.Activator;
+import org.eclipse.egit.core.CoreText;
+import org.eclipse.team.core.RepositoryProvider;
+
+/**
+ * Disconnects the Git team provider from a project.
+ * <p>
+ * Once disconnected, Git operations will no longer be available on the project.
+ * </p>
+ */
+public class DisconnectProviderOperation implements IWorkspaceRunnable {
+ private final Collection projectList;
+
+ /**
+ * Create a new disconnect operation.
+ *
+ * @param projs
+ * the collection of {@link IProject}s which should be
+ * disconnected from the Git team provider, and returned to
+ * untracked/unmanaged status.
+ */
+ public DisconnectProviderOperation(final Collection projs) {
+ projectList = projs;
+ }
+
+ public void run(IProgressMonitor m) throws CoreException {
+ if (m == null) {
+ m = new NullProgressMonitor();
+ }
+
+ m.beginTask(CoreText.DisconnectProviderOperation_disconnecting,
+ projectList.size() * 200);
+ try {
+ for (Object obj : projectList) {
+ obj = ((IAdaptable)obj).getAdapter(IResource.class);
+ if (obj instanceof IProject) {
+ final IProject p = (IProject) obj;
+
+ Activator.trace("disconnect " + p.getName());
+ unmarkTeamPrivate(p);
+ RepositoryProvider.unmap(p);
+ m.worked(100);
+
+ p.refreshLocal(IResource.DEPTH_INFINITE,
+ new SubProgressMonitor(m, 100));
+ } else {
+ m.worked(200);
+ }
+ }
+ } finally {
+ m.done();
+ }
+ }
+
+ private void unmarkTeamPrivate(final IContainer p) throws CoreException {
+ final IResource[] c;
+ c = p.members(IContainer.INCLUDE_TEAM_PRIVATE_MEMBERS);
+ if (c != null) {
+ for (int k = 0; k < c.length; k++) {
+ if (c[k] instanceof IContainer) {
+ unmarkTeamPrivate((IContainer) c[k]);
+ }
+ if (c[k].isTeamPrivateMember()) {
+ Activator.trace("notTeamPrivate " + c[k]);
+ c[k].setTeamPrivateMember(false);
+ }
+ }
+ }
+ }
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/op/ListRemoteOperation.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/op/ListRemoteOperation.java
new file mode 100644
index 0000000000..6963d750d2
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/op/ListRemoteOperation.java
@@ -0,0 +1,105 @@
+/*******************************************************************************
+ * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.egit.core.op;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.Collection;
+import java.util.Map;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.egit.core.CoreText;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jgit.errors.NotSupportedException;
+import org.eclipse.jgit.errors.TransportException;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.transport.Connection;
+import org.eclipse.jgit.transport.Transport;
+import org.eclipse.jgit.transport.URIish;
+
+/**
+ * Operation of listing remote repository advertised refs.
+ */
+public class ListRemoteOperation implements IRunnableWithProgress {
+ private final Repository localDb;
+
+ private final URIish uri;
+
+ private Map<String, Ref> remoteRefsMap;
+
+ /**
+ * Create listing operation for specified local repository (needed by
+ * transport) and remote repository URI.
+ *
+ * @param localDb
+ * local repository (needed for transport) where fetch would
+ * occur.
+ * @param uri
+ * URI of remote repository to list.
+ */
+ public ListRemoteOperation(final Repository localDb, final URIish uri) {
+ this.localDb = localDb;
+ this.uri = uri;
+ }
+
+ /**
+ * @return collection of refs advertised by remote side.
+ * @throws IllegalStateException
+ * if error occurred during earlier remote refs listing.
+ */
+ public Collection<Ref> getRemoteRefs() {
+ checkState();
+ return remoteRefsMap.values();
+ }
+
+ /**
+ * @param refName
+ * remote ref name to search for.
+ * @return ref with specified refName or null if not found.
+ * @throws IllegalStateException
+ * if error occurred during earlier remote refs listing.
+ */
+ public Ref getRemoteRef(final String refName) {
+ checkState();
+ return remoteRefsMap.get(refName);
+ }
+
+ public void run(IProgressMonitor pm) throws InvocationTargetException,
+ InterruptedException {
+ Transport transport = null;
+ Connection connection = null;
+ try {
+ transport = Transport.open(localDb, uri);
+
+ if (pm != null)
+ pm.beginTask(CoreText.ListRemoteOperation_title,
+ IProgressMonitor.UNKNOWN);
+ connection = transport.openFetch();
+ remoteRefsMap = connection.getRefsMap();
+ } catch (NotSupportedException e) {
+ throw new InvocationTargetException(e);
+ } catch (TransportException e) {
+ throw new InvocationTargetException(e);
+ } finally {
+ if (connection != null)
+ connection.close();
+ if (transport != null)
+ transport.close();
+ if (pm != null)
+ pm.done();
+ }
+ }
+
+ private void checkState() {
+ if (remoteRefsMap == null)
+ throw new IllegalStateException(
+ "Error occurred during remote repo listing, no refs available");
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/op/PushOperation.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/op/PushOperation.java
new file mode 100644
index 0000000000..37ae51a3dc
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/op/PushOperation.java
@@ -0,0 +1,154 @@
+/*******************************************************************************
+ * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.egit.core.op;
+
+import java.lang.reflect.InvocationTargetException;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.SubProgressMonitor;
+import org.eclipse.egit.core.CoreText;
+import org.eclipse.egit.core.EclipseGitProgressTransformer;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.jgit.errors.NoRemoteRepositoryException;
+import org.eclipse.jgit.errors.NotSupportedException;
+import org.eclipse.jgit.errors.TransportException;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.transport.PushResult;
+import org.eclipse.jgit.transport.RemoteConfig;
+import org.eclipse.jgit.transport.Transport;
+import org.eclipse.jgit.transport.URIish;
+
+/**
+ * Push operation: pushing from local repository to one or many remote ones.
+ */
+public class PushOperation implements IRunnableWithProgress {
+ private static final int WORK_UNITS_PER_TRANSPORT = 10;
+
+ private final Repository localDb;
+
+ private final PushOperationSpecification specification;
+
+ private final boolean dryRun;
+
+ private final RemoteConfig rc;
+
+ private final PushOperationResult operationResult = new PushOperationResult();
+
+ /**
+ * Create push operation for provided specification.
+ * <p>
+ * Operation is not performed within constructor,
+ * {@link #run(IProgressMonitor)} method must be called for that.
+ *
+ * @param localDb
+ * local repository.
+ * @param specification
+ * specification of ref updates for remote repositories.
+ * @param rc
+ * optional remote config to apply on used transports. May be
+ * null.
+ * @param dryRun
+ * true if push operation should just check for possible result
+ * and not really update remote refs, false otherwise - when push
+ * should act normally.
+ */
+ public PushOperation(final Repository localDb,
+ final PushOperationSpecification specification,
+ final boolean dryRun, final RemoteConfig rc) {
+ this.localDb = localDb;
+ this.specification = specification;
+ this.dryRun = dryRun;
+ this.rc = rc;
+ }
+
+ /**
+ * @return push operation result.
+ */
+ public PushOperationResult getOperationResult() {
+ return operationResult;
+ }
+
+ /**
+ * @return operation specification, as provided in constructor.
+ */
+ public PushOperationSpecification getSpecification() {
+ return specification;
+ }
+
+ /**
+ * Execute operation and store result. Operation is executed independently
+ * on each remote repository.
+ * <p>
+ *
+ * @throws InvocationTargetException
+ * Cause of this exceptions may include
+ * {@link TransportException}, {@link NotSupportedException} or
+ * some unexpected {@link RuntimeException}.
+ * @see IRunnableWithProgress#run(IProgressMonitor)
+ */
+ public void run(IProgressMonitor monitor) throws InvocationTargetException {
+ if (monitor == null)
+ monitor = new NullProgressMonitor();
+
+ final int totalWork = specification.getURIsNumber()
+ * WORK_UNITS_PER_TRANSPORT;
+ if (dryRun)
+ monitor.beginTask(CoreText.PushOperation_taskNameDryRun, totalWork);
+ else
+ monitor.beginTask(CoreText.PushOperation_taskNameNormalRun,
+ totalWork);
+
+ for (final URIish uri : specification.getURIs()) {
+ final SubProgressMonitor subMonitor = new SubProgressMonitor(
+ monitor, WORK_UNITS_PER_TRANSPORT,
+ SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK);
+ Transport transport = null;
+ try {
+ if (monitor.isCanceled()) {
+ operationResult.addOperationResult(uri,
+ CoreText.PushOperation_resultCancelled);
+ continue;
+ }
+ transport = Transport.open(localDb, uri);
+
+ if (rc != null)
+ transport.applyConfig(rc);
+ transport.setDryRun(dryRun);
+ final EclipseGitProgressTransformer gitSubMonitor = new EclipseGitProgressTransformer(
+ subMonitor);
+ final PushResult pr = transport.push(gitSubMonitor,
+ specification.getRefUpdates(uri));
+ operationResult.addOperationResult(uri, pr);
+ } catch (final NoRemoteRepositoryException e) {
+ operationResult.addOperationResult(uri, NLS.bind(
+ CoreText.PushOperation_resultNoServiceError, e
+ .getMessage()));
+ } catch (final TransportException e) {
+ operationResult.addOperationResult(uri, NLS.bind(
+ CoreText.PushOperation_resultTransportError, e
+ .getMessage()));
+ } catch (final NotSupportedException e) {
+ operationResult.addOperationResult(uri, NLS.bind(
+ CoreText.PushOperation_resultNotSupported, e
+ .getMessage()));
+ } finally {
+ if (transport != null) {
+ transport.close();
+ }
+ // Dirty trick to get things always working.
+ subMonitor.beginTask("", WORK_UNITS_PER_TRANSPORT); //$NON-NLS-1$
+ subMonitor.done();
+ subMonitor.done();
+ }
+ }
+ monitor.done();
+ }
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/op/PushOperationResult.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/op/PushOperationResult.java
new file mode 100644
index 0000000000..520951d213
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/op/PushOperationResult.java
@@ -0,0 +1,274 @@
+/*******************************************************************************
+ * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.egit.core.op;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Set;
+
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.transport.PushResult;
+import org.eclipse.jgit.transport.RemoteRefUpdate;
+import org.eclipse.jgit.transport.URIish;
+
+/**
+ * Data class for storing push operation results for each remote repository/URI
+ * being part of push operation.
+ * <p>
+ * One instance of this class is dedicated for result of one push operation:
+ * either to one URI or to many URIs.
+ *
+ * @see PushOperation
+ */
+public class PushOperationResult {
+ private LinkedHashMap<URIish, Entry> urisEntries;
+
+ /**
+ * Construct empty push operation result.
+ */
+ PushOperationResult() {
+ this.urisEntries = new LinkedHashMap<URIish, Entry>();
+ }
+
+ /**
+ * Add push result for the repository (URI) with successful connection.
+ *
+ * @param uri
+ * remote repository URI.
+ * @param result
+ * push result.
+ */
+ public void addOperationResult(final URIish uri, final PushResult result) {
+ urisEntries.put(uri, new Entry(result));
+ }
+
+ /**
+ * Add error message for the repository (URI) with unsuccessful connection.
+ *
+ * @param uri
+ * remote repository URI.
+ * @param errorMessage
+ * failure error message.
+ */
+ public void addOperationResult(final URIish uri, final String errorMessage) {
+ urisEntries.put(uri, new Entry(errorMessage));
+ }
+
+ /**
+ * @return set of remote repositories URIish. Set is ordered in addition
+ * sequence, which is usually the same as that from
+ * {@link PushOperationSpecification}.
+ */
+ public Set<URIish> getURIs() {
+ return Collections.unmodifiableSet(urisEntries.keySet());
+ }
+
+ /**
+ * @param uri
+ * remote repository URI.
+ * @return true if connection was successful for this repository (URI),
+ * false if this operation ended with unsuccessful connection.
+ */
+ public boolean isSuccessfulConnection(final URIish uri) {
+ return urisEntries.get(uri).isSuccessfulConnection();
+ }
+
+ /**
+ * @return true if connection was successful for any repository (URI), false
+ * otherwise.
+ */
+ public boolean isSuccessfulConnectionForAnyURI() {
+ for (final URIish uri : getURIs()) {
+ if (isSuccessfulConnection(uri))
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * @param uri
+ * remote repository URI.
+ * @return push result for this repository (URI) or null if operation ended
+ * with unsuccessful connection for this URI.
+ */
+ public PushResult getPushResult(final URIish uri) {
+ return urisEntries.get(uri).getResult();
+ }
+
+ /**
+ * @param uri
+ * remote repository URI.
+ * @return error message for this repository (URI) or null if operation
+ * ended with successful connection for this URI.
+ */
+ public String getErrorMessage(final URIish uri) {
+ return urisEntries.get(uri).getErrorMessage();
+ }
+
+ /**
+ * @return string being list of failed URIs with their error messages.
+ */
+ public String getErrorStringForAllURis() {
+ final StringBuilder sb = new StringBuilder();
+ boolean first = true;
+ for (final URIish uri : getURIs()) {
+ if (first)
+ first = false;
+ else
+ sb.append(", ");
+ sb.append(uri);
+ sb.append(" (");
+ sb.append(getErrorMessage(uri));
+ sb.append(")");
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Derive push operation specification from this push operation result.
+ * <p>
+ * Specification is created basing on URIs of remote repositories in this
+ * result that completed without connection errors, and remote ref updates
+ * from push results.
+ * <p>
+ * This method is targeted to provide support for 2-stage push, where first
+ * operation is dry run for user confirmation and second one is a real
+ * operation.
+ *
+ * @param requireUnchanged
+ * if true, newly created copies of remote ref updates have
+ * expected old object id set to previously advertised ref value
+ * (remote ref won't be updated if it change in the mean time),
+ * if false, newly create copies of remote ref updates have
+ * expected object id set up as in this result source
+ * specification.
+ * @return derived specification for another push operation.
+ * @throws IOException
+ * when some previously locally available source ref is not
+ * available anymore, or some error occurred during creation
+ * locally tracking ref update.
+ *
+ */
+ public PushOperationSpecification deriveSpecification(
+ final boolean requireUnchanged) throws IOException {
+ final PushOperationSpecification spec = new PushOperationSpecification();
+ for (final URIish uri : getURIs()) {
+ final PushResult pr = getPushResult(uri);
+ if (pr == null)
+ continue;
+
+ final Collection<RemoteRefUpdate> oldUpdates = pr
+ .getRemoteUpdates();
+ final ArrayList<RemoteRefUpdate> newUpdates = new ArrayList<RemoteRefUpdate>(
+ oldUpdates.size());
+ for (final RemoteRefUpdate rru : oldUpdates) {
+ final ObjectId expectedOldObjectId;
+ if (requireUnchanged) {
+ final Ref advertisedRef = getPushResult(uri)
+ .getAdvertisedRef(rru.getRemoteName());
+ if (advertisedRef == null)
+ expectedOldObjectId = ObjectId.zeroId();
+ else
+ expectedOldObjectId = advertisedRef.getObjectId();
+ } else
+ expectedOldObjectId = rru.getExpectedOldObjectId();
+ final RemoteRefUpdate newRru = new RemoteRefUpdate(rru,
+ expectedOldObjectId);
+ newUpdates.add(newRru);
+ }
+ spec.addURIRefUpdates(uri, newUpdates);
+ }
+ return spec;
+ }
+
+ /**
+ * This implementation returns true if all following conditions are met:
+ * <ul>
+ * <li>both objects result have the same set successfully connected
+ * repositories (URIs) - unsuccessful connections are discarded, AND <li>
+ * remote ref updates must match for each successful connection in sense of
+ * equal remoteName, equal status and equal newObjectId value.</li>
+ * </ul>
+ *
+ * @see Object#equals(Object)
+ * @param obj
+ * other push operation result to compare to.
+ * @return true if object is equal to this one in terms of conditions
+ * described above, false otherwise.
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (!(obj instanceof PushOperationResult))
+ return false;
+
+ final PushOperationResult other = (PushOperationResult) obj;
+
+ // Check successful connections/URIs two-ways:
+ final Set<URIish> otherURIs = other.getURIs();
+ for (final URIish uri : getURIs()) {
+ if (isSuccessfulConnection(uri)
+ && (!otherURIs.contains(uri) || !other
+ .isSuccessfulConnection(uri)))
+ return false;
+ }
+ for (final URIish uri : other.getURIs()) {
+ if (other.isSuccessfulConnection(uri)
+ && (!urisEntries.containsKey(uri) || !isSuccessfulConnection(uri)))
+ return false;
+ }
+
+ for (final URIish uri : getURIs()) {
+ if (!isSuccessfulConnection(uri))
+ continue;
+
+ final PushResult otherPushResult = other.getPushResult(uri);
+ for (final RemoteRefUpdate rru : getPushResult(uri)
+ .getRemoteUpdates()) {
+ final RemoteRefUpdate otherRru = otherPushResult
+ .getRemoteUpdate(rru.getRemoteName());
+ if (otherRru == null)
+ return false;
+ if (otherRru.getStatus() != rru.getStatus()
+ || otherRru.getNewObjectId() != rru.getNewObjectId())
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static class Entry {
+ private String errorMessage;
+
+ private PushResult result;
+
+ Entry(final PushResult result) {
+ this.result = result;
+ }
+
+ Entry(final String errorMessage) {
+ this.errorMessage = errorMessage;
+ }
+
+ boolean isSuccessfulConnection() {
+ return result != null;
+ }
+
+ String getErrorMessage() {
+ return errorMessage;
+ }
+
+ PushResult getResult() {
+ return result;
+ }
+ }
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/op/PushOperationSpecification.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/op/PushOperationSpecification.java
new file mode 100644
index 0000000000..6369f5a530
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/op/PushOperationSpecification.java
@@ -0,0 +1,83 @@
+/*******************************************************************************
+ * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.egit.core.op;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Set;
+
+import org.eclipse.jgit.transport.RemoteRefUpdate;
+import org.eclipse.jgit.transport.URIish;
+
+/**
+ * Data class storing push operation update specifications for each remote
+ * repository.
+ * <p>
+ * One instance is dedicated for one push operation: either push to one URI or
+ * to many URIs.
+ *
+ * @see PushOperation
+ */
+public class PushOperationSpecification {
+ private LinkedHashMap<URIish, Collection<RemoteRefUpdate>> urisRefUpdates;
+
+ /**
+ * Create empty instance of specification.
+ * <p>
+ * URIs and ref updates should be configured
+ * {@link #addURIRefUpdates(URIish, Collection)} method.
+ */
+ public PushOperationSpecification() {
+ this.urisRefUpdates = new LinkedHashMap<URIish, Collection<RemoteRefUpdate>>();
+ }
+
+ /**
+ * Add remote repository URI with ref updates specification.
+ * <p>
+ * Ref updates are not in constructor - pay attention to not share them
+ * between different URIs ref updates or push operations.
+ * <p>
+ * Note that refUpdates can differ between URIs <b>only</b> by expected old
+ * object id field: {@link RemoteRefUpdate#getExpectedOldObjectId()}.
+ *
+ * @param uri
+ * remote repository URI.
+ * @param refUpdates
+ * collection of remote ref updates specifications.
+ */
+ public void addURIRefUpdates(final URIish uri,
+ Collection<RemoteRefUpdate> refUpdates) {
+ urisRefUpdates.put(uri, refUpdates);
+ }
+
+ /**
+ * @return set of remote repositories URIish. Set is ordered in addition
+ * sequence.
+ */
+ public Set<URIish> getURIs() {
+ return Collections.unmodifiableSet(urisRefUpdates.keySet());
+ }
+
+ /**
+ * @return number of remote repositories URI for this push operation.
+ */
+ public int getURIsNumber() {
+ return urisRefUpdates.keySet().size();
+ }
+
+ /**
+ * @param uri
+ * remote repository URI.
+ * @return remote ref updates as specified by user for this URI.
+ */
+ public Collection<RemoteRefUpdate> getRefUpdates(final URIish uri) {
+ return Collections.unmodifiableCollection(urisRefUpdates.get(uri));
+ }
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/op/ResetOperation.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/op/ResetOperation.java
new file mode 100644
index 0000000000..1b046632b6
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/op/ResetOperation.java
@@ -0,0 +1,244 @@
+/*******************************************************************************
+ * Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
+ * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.egit.core.op;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspaceRunnable;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.jgit.lib.Commit;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.GitIndex;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.RefLogWriter;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.Tag;
+import org.eclipse.jgit.lib.Tree;
+import org.eclipse.jgit.lib.WorkDirCheckout;
+
+/**
+ * A class for changing a ref and possibly index and workdir too.
+ */
+public class ResetOperation implements IWorkspaceRunnable {
+ /**
+ * Kind of reset
+ */
+ public enum ResetType {
+ /**
+ * Just change the ref. The index and workdir are not changed.
+ */
+ SOFT,
+
+ /**
+ * Change the ref and the index. The workdir is not changed.
+ */
+ MIXED,
+
+ /**
+ * Change the ref, the index and the workdir
+ */
+ HARD
+ }
+
+ private final Repository repository;
+ private final String refName;
+ private final ResetType type;
+
+ private Commit commit;
+ private Commit previousCommit;
+ private Tree newTree;
+ private GitIndex index;
+
+ /**
+ * Construct a {@link ResetOperation}
+ *
+ * @param repository
+ * @param refName
+ * @param type
+ */
+ public ResetOperation(Repository repository, String refName, ResetType type) {
+ this.repository = repository;
+ this.refName = refName;
+ this.type = type;
+ }
+
+ public void run(IProgressMonitor monitor) throws CoreException {
+ monitor.beginTask("Performing " + type.toString().toLowerCase() + " reset to " + refName, 7);
+
+ mapObjects();
+ monitor.worked(1);
+
+ writeRef();
+ monitor.worked(1);
+
+ if (type != ResetType.SOFT) {
+ if (type == ResetType.MIXED)
+ resetIndex();
+ else
+ readIndex();
+ writeIndex();
+ }
+ monitor.worked(1);
+
+ if (type == ResetType.HARD) {
+ checkoutIndex();
+ }
+ monitor.worked(1);
+
+ if (type != ResetType.SOFT) {
+ refreshIndex();
+ }
+ monitor.worked(1);
+
+ writeReflogs();
+ monitor.worked(1);
+
+ refreshProjects();
+
+ monitor.done();
+ }
+
+ private void refreshIndex() throws TeamException {
+// File workdir = repository.getDirectory().getParentFile();
+// for (Entry e : newIndex.getMembers()) {
+// try {
+// e.update(new File(workdir, e.getName()));
+// } catch (IOException ignore) {}
+// }
+ try {
+ index.write();
+ } catch (IOException e1) {
+ throw new TeamException("Writing index", e1);
+ }
+ }
+
+ private void refreshProjects() {
+ final IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects();
+ final File parentFile = repository.getWorkDir();
+ for (IProject p : projects) {
+ final File file = p.getLocation().toFile();
+ if (file.getAbsolutePath().startsWith(parentFile.getAbsolutePath())) {
+ try {
+ System.out.println("Refreshing " + p);
+ p.refreshLocal(IResource.DEPTH_INFINITE, null);
+ } catch (CoreException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ private void mapObjects() throws TeamException {
+ final ObjectId commitId;
+ try {
+ commitId = repository.resolve(refName);
+ } catch (IOException e) {
+ throw new TeamException("looking up ref " + refName, e);
+ }
+ try {
+ commit = repository.mapCommit(commitId);
+ } catch (IOException e) {
+ try {
+ Tag t = repository.mapTag(refName, commitId);
+ commit = repository.mapCommit(t.getObjId());
+ } catch (IOException e2) {
+ throw new TeamException("looking up commit " + commitId, e2);
+ }
+ }
+
+ try {
+ previousCommit = repository.mapCommit(repository.resolve(Constants.HEAD));
+ } catch (IOException e) {
+ throw new TeamException("looking up HEAD commit", e);
+ }
+ }
+
+ private void writeRef() throws TeamException {
+ try {
+ final RefUpdate ru = repository.updateRef(Constants.HEAD);
+ ru.setNewObjectId(commit.getCommitId());
+ ru.setRefLogMessage("reset", false);
+ if (ru.forceUpdate() == RefUpdate.Result.LOCK_FAILURE)
+ throw new TeamException("Can't update " + ru.getName());
+ } catch (IOException e) {
+ throw new TeamException("Updating " + Constants.HEAD + " failed", e);
+ }
+ }
+
+ private void readIndex() throws TeamException {
+ try {
+ newTree = commit.getTree();
+ index = repository.getIndex();
+ } catch (IOException e) {
+ throw new TeamException("Reading index", e);
+ }
+ }
+
+ private void resetIndex() throws TeamException {
+ try {
+ newTree = commit.getTree();
+ index = repository.getIndex();
+ index.readTree(newTree);
+ } catch (IOException e) {
+ throw new TeamException("Reading index", e);
+ }
+ }
+
+ private void writeIndex() throws CoreException {
+ try {
+ index.write();
+ } catch (IOException e) {
+ throw new TeamException("Writing index", e);
+ }
+ }
+
+ private void checkoutIndex() throws TeamException {
+ final File parentFile = repository.getWorkDir();
+ try {
+ WorkDirCheckout workDirCheckout =
+ new WorkDirCheckout(repository, parentFile, index, newTree);
+ workDirCheckout.setFailOnConflict(false);
+ workDirCheckout.checkout();
+ } catch (IOException e) {
+ throw new TeamException("mapping tree for commit", e);
+ }
+ }
+
+
+ private void writeReflog(String reflogRelPath) throws IOException {
+ String name = refName;
+ if (name.startsWith("refs/heads/"))
+ name = name.substring(11);
+ if (name.startsWith("refs/remotes/"))
+ name = name.substring(13);
+
+ String message = "reset --" + type.toString().toLowerCase() + " " + name;
+
+ RefLogWriter.writeReflog(repository, previousCommit.getCommitId(), commit.getCommitId(), message, reflogRelPath);
+ }
+
+ private void writeReflogs() throws TeamException {
+ try {
+ writeReflog(Constants.HEAD);
+ writeReflog(repository.getFullBranch());
+ } catch (IOException e) {
+ throw new TeamException("Writing reflogs", e);
+ }
+ }
+}
+
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/op/TrackOperation.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/op/TrackOperation.java
new file mode 100644
index 0000000000..cd431a4847
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/op/TrackOperation.java
@@ -0,0 +1,149 @@
+/*******************************************************************************
+ * Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
+ * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.egit.core.op;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.IdentityHashMap;
+import java.util.Iterator;
+
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceVisitor;
+import org.eclipse.core.resources.IWorkspaceRunnable;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.egit.core.Activator;
+import org.eclipse.egit.core.CoreText;
+import org.eclipse.egit.core.project.RepositoryMapping;
+import org.eclipse.team.core.Team;
+import org.eclipse.jgit.lib.GitIndex;
+import org.eclipse.jgit.lib.GitIndex.Entry;
+
+/**
+ * Add one or more new files/folders to the Git repository.
+ * <p>
+ * Accepts a collection of resources (files and/or directories) which should be
+ * added to the their corresponding Git repositories. Resources in the
+ * collection can be associated with multiple repositories. The operation will
+ * automatically associate each resource with the nearest containing Git
+ * repository.
+ * </p>
+ * <p>
+ * Resources are only scheduled for addition in the index.
+ * </p>
+ */
+public class TrackOperation implements IWorkspaceRunnable {
+ private final Collection rsrcList;
+
+ /**
+ * Create a new operation to track additional files/folders.
+ *
+ * @param rsrcs
+ * collection of {@link IResource}s which should be added to the
+ * relevant Git repositories.
+ */
+ public TrackOperation(final Collection rsrcs) {
+ rsrcList = rsrcs;
+ }
+
+ public void run(IProgressMonitor m) throws CoreException {
+ if (m == null) {
+ m = new NullProgressMonitor();
+ }
+
+ final IdentityHashMap<RepositoryMapping, Boolean> tomerge = new IdentityHashMap<RepositoryMapping, Boolean>();
+ m.beginTask(CoreText.AddOperation_adding, rsrcList.size() * 200);
+ try {
+ for (Object obj : rsrcList) {
+ obj = ((IAdaptable)obj).getAdapter(IResource.class);
+ if (obj instanceof IResource) {
+ final IResource toAdd = (IResource)obj;
+ final RepositoryMapping rm = RepositoryMapping.getMapping(toAdd);
+ final GitIndex index = rm.getRepository().getIndex();
+
+ if (obj instanceof IFile) {
+ String repoPath = rm.getRepoRelativePath((IResource) obj);
+ Entry entry = index.getEntry(repoPath);
+ if (entry != null) {
+ if (!entry.isAssumedValid()) {
+ System.out.println("Already tracked - skipping");
+ continue;
+ }
+ }
+ }
+
+ tomerge.put(rm, Boolean.TRUE);
+ if (toAdd instanceof IContainer) {
+ ((IContainer)toAdd).accept(new IResourceVisitor() {
+ public boolean visit(IResource resource) throws CoreException {
+ try {
+ String repoPath = rm.getRepoRelativePath(resource);
+ // We use add to reset the assume valid bit, so we check the bit
+ // first. If a resource within a ignored folder is marked
+ // we ignore it here, i.e. there is no way to unmark it expect
+ // by explicitly selecting and invoking track on it.
+ boolean isIgnored = Team.isIgnoredHint(resource);
+ if (resource.getType() == IResource.FILE) {
+ Entry entry = index.getEntry(repoPath);
+ if (!isIgnored || entry != null && entry.isAssumedValid()) {
+ entry = index.add(rm.getWorkDir(), new File(rm.getWorkDir(), repoPath));
+ entry.setAssumeValid(false);
+ }
+ }
+ if (isIgnored)
+ return false;
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ throw Activator.error(CoreText.AddOperation_failed, e);
+ }
+ return true;
+ }
+ },IResource.DEPTH_INFINITE, IContainer.EXCLUDE_DERIVED);
+ } else {
+ Entry entry = index.add(rm.getWorkDir(), new File(rm.getWorkDir(),rm.getRepoRelativePath(toAdd)));
+ entry.setAssumeValid(false);
+
+ }
+ }
+ m.worked(200);
+ }
+ for (RepositoryMapping rm : tomerge.keySet()) {
+ m.setTaskName("Writing index for "+rm.getRepository().getDirectory());
+ rm.getRepository().getIndex().write();
+ }
+ } catch (RuntimeException e) {
+ e.printStackTrace();
+ throw Activator.error(CoreText.AddOperation_failed, e);
+ } catch (IOException e) {
+ e.printStackTrace();
+ throw Activator.error(CoreText.AddOperation_failed, e);
+ } finally {
+ try {
+ final Iterator i = tomerge.keySet().iterator();
+ while (i.hasNext()) {
+ final RepositoryMapping r = (RepositoryMapping) i.next();
+ r.getRepository().getIndex().read();
+ r.fireRepositoryChanged();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ m.done();
+ }
+ }
+ }
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/op/UntrackOperation.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/op/UntrackOperation.java
new file mode 100644
index 0000000000..39bd827ae4
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/op/UntrackOperation.java
@@ -0,0 +1,127 @@
+/*******************************************************************************
+ * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
+ * Copyright (C) 2008, Google Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.egit.core.op;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.IdentityHashMap;
+import java.util.Map;
+
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspaceRunnable;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.egit.core.Activator;
+import org.eclipse.egit.core.CoreText;
+import org.eclipse.egit.core.project.GitProjectData;
+import org.eclipse.egit.core.project.RepositoryMapping;
+import org.eclipse.jgit.dircache.DirCache;
+import org.eclipse.jgit.dircache.DirCacheEditor;
+import org.eclipse.jgit.lib.Repository;
+
+/**
+ * Remove one or more existing files/folders from the Git repository.
+ * <p>
+ * Accepts a collection of resources (files and/or directories) which should be
+ * removed from the their corresponding Git repositories. Resources in the
+ * collection can be associated with multiple repositories. The operation will
+ * automatically remove each resource from the correct Git repository.
+ * </p>
+ * <p>
+ * Resources are only scheduled for removal in the index-
+ * </p>
+ */
+public class UntrackOperation implements IWorkspaceRunnable {
+ private final Collection rsrcList;
+
+ private final IdentityHashMap<Repository, DirCacheEditor> edits;
+
+ private final IdentityHashMap<RepositoryMapping, Object> mappings;
+
+ /**
+ * Create a new operation to stop tracking existing files/folders.
+ *
+ * @param rsrcs
+ * collection of {@link IResource}s which should be removed from
+ * the relevant Git repositories.
+ */
+ public UntrackOperation(final Collection rsrcs) {
+ rsrcList = rsrcs;
+ edits = new IdentityHashMap<Repository, DirCacheEditor>();
+ mappings = new IdentityHashMap<RepositoryMapping, Object>();
+ }
+
+ public void run(IProgressMonitor m) throws CoreException {
+ if (m == null)
+ m = new NullProgressMonitor();
+
+ edits.clear();
+ mappings.clear();
+
+ m.beginTask(CoreText.AddOperation_adding, rsrcList.size() * 200);
+ try {
+ for (Object obj : rsrcList) {
+ obj = ((IAdaptable) obj).getAdapter(IResource.class);
+ if (obj instanceof IResource)
+ remove((IResource) obj);
+ m.worked(200);
+ }
+
+ for (Map.Entry<Repository, DirCacheEditor> e : edits.entrySet()) {
+ final Repository db = e.getKey();
+ final DirCacheEditor editor = e.getValue();
+ m.setTaskName("Writing index for " + db.getDirectory());
+ editor.commit();
+ }
+ } catch (RuntimeException e) {
+ throw Activator.error(CoreText.UntrackOperation_failed, e);
+ } catch (IOException e) {
+ throw Activator.error(CoreText.UntrackOperation_failed, e);
+ } finally {
+ for (final RepositoryMapping rm : mappings.keySet())
+ rm.fireRepositoryChanged();
+ edits.clear();
+ mappings.clear();
+ m.done();
+ }
+ }
+
+ private void remove(final IResource path) throws CoreException {
+ final IProject proj = path.getProject();
+ final GitProjectData pd = GitProjectData.get(proj);
+ if (pd == null)
+ return;
+ final RepositoryMapping rm = pd.getRepositoryMapping(path);
+ if (rm == null)
+ return;
+ final Repository db = rm.getRepository();
+
+ DirCacheEditor e = edits.get(db);
+ if (e == null) {
+ try {
+ e = DirCache.lock(db).editor();
+ } catch (IOException err) {
+ throw Activator.error(CoreText.UntrackOperation_failed, err);
+ }
+ edits.put(db, e);
+ mappings.put(rm, rm);
+ }
+
+ if (path instanceof IContainer)
+ e.add(new DirCacheEditor.DeleteTree(rm.getRepoRelativePath(path)));
+ else
+ e.add(new DirCacheEditor.DeletePath(rm.getRepoRelativePath(path)));
+ }
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/op/UpdateOperation.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/op/UpdateOperation.java
new file mode 100644
index 0000000000..1120da9935
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/op/UpdateOperation.java
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2007, Shawn O. Pearce <spearce@spearce.org>
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.egit.core.op;
+
+import java.util.Collection;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspaceRunnable;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.egit.core.internal.UpdateJob;
+
+/**
+ * Updates the Git index for the selected resources. Only tracked resources
+ * are updated.
+ * <p>
+ * Accepts a collection of resources (files and/or directories) whose content
+ * should be updated in the corresponding Git repositories. Resources in the
+ * collection can be associated with multiple repositories.
+ * </p>
+ */
+public class UpdateOperation implements IWorkspaceRunnable {
+ private final Collection rsrcList;
+
+ /**
+ * Create a new operation to update files/folders.
+ *
+ * @param rsrcs
+ * collection of {@link IResource}s which should be added to the
+ * relevant Git repositories.
+ */
+ public UpdateOperation(final Collection rsrcs) {
+ rsrcList = rsrcs;
+ }
+
+ public void run(IProgressMonitor m) throws CoreException {
+ new UpdateJob(rsrcList).schedule();
+ }
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/project/GitProjectData.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/project/GitProjectData.java
new file mode 100644
index 0000000000..32ac2f0fe1
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/project/GitProjectData.java
@@ -0,0 +1,491 @@
+/*******************************************************************************
+ * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
+ * Copyright (C) 2008, Google Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.egit.core.project;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceChangeEvent;
+import org.eclipse.core.resources.IResourceChangeListener;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.Preferences;
+import org.eclipse.core.runtime.QualifiedName;
+import org.eclipse.egit.core.Activator;
+import org.eclipse.egit.core.CoreText;
+import org.eclipse.egit.core.GitCorePreferences;
+import org.eclipse.egit.core.GitProvider;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.team.core.RepositoryProvider;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.WindowCache;
+import org.eclipse.jgit.lib.WindowCacheConfig;
+
+/**
+ * This class keeps information about how a project is mapped to
+ * a Git repository.
+ */
+public class GitProjectData {
+ private static final Map<IProject, GitProjectData> projectDataCache = new HashMap<IProject, GitProjectData>();
+
+ private static final Map<File, WeakReference> repositoryCache = new HashMap<File, WeakReference>();
+
+ private static Set<RepositoryChangeListener> repositoryChangeListeners = new HashSet<RepositoryChangeListener>();
+
+ @SuppressWarnings("synthetic-access")
+ private static final IResourceChangeListener rcl = new RCL();
+
+ private static class RCL implements IResourceChangeListener {
+ @SuppressWarnings("synthetic-access")
+ public void resourceChanged(final IResourceChangeEvent event) {
+ switch (event.getType()) {
+ case IResourceChangeEvent.PRE_CLOSE:
+ uncache((IProject) event.getResource());
+ break;
+ case IResourceChangeEvent.PRE_DELETE:
+ delete((IProject) event.getResource());
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ private static QualifiedName MAPPING_KEY = new QualifiedName(
+ GitProjectData.class.getName(), "RepositoryMapping");
+
+ /**
+ * Start listening for resource changes.
+ *
+ * @param includeChange true to listen to content changes
+ */
+ public static void attachToWorkspace(final boolean includeChange) {
+ trace("attachToWorkspace - addResourceChangeListener");
+ ResourcesPlugin.getWorkspace().addResourceChangeListener(
+ rcl,
+ (includeChange ? IResourceChangeEvent.POST_CHANGE : 0)
+ | IResourceChangeEvent.PRE_CLOSE
+ | IResourceChangeEvent.PRE_DELETE);
+ }
+
+ /**
+ * Stop listening to resource changes
+ */
+ public static void detachFromWorkspace() {
+ trace("detachFromWorkspace - removeResourceChangeListener");
+ ResourcesPlugin.getWorkspace().removeResourceChangeListener(rcl);
+ }
+
+ /**
+ * Register a new listener for repository modification events.
+ * <p>
+ * This is a no-op if <code>objectThatCares</code> has already been
+ * registered.
+ * </p>
+ *
+ * @param objectThatCares
+ * the new listener to register. Must not be null.
+ */
+ public static synchronized void addRepositoryChangeListener(
+ final RepositoryChangeListener objectThatCares) {
+ if (objectThatCares == null)
+ throw new NullPointerException();
+ repositoryChangeListeners.add(objectThatCares);
+ }
+
+ /**
+ * Remove a registered {@link RepositoryChangeListener}
+ *
+ * @param objectThatCares
+ * The listener to remove
+ */
+ public static synchronized void removeRepositoryChangeListener(
+ final RepositoryChangeListener objectThatCares) {
+ repositoryChangeListeners.remove(objectThatCares);
+ }
+
+ /**
+ * Notify registered {@link RepositoryChangeListener}s of a change.
+ *
+ * @param which
+ * the repository which has had changes occur within it.
+ */
+ static void fireRepositoryChanged(final RepositoryMapping which) {
+ for (RepositoryChangeListener listener : getRepositoryChangeListeners())
+ listener.repositoryChanged(which);
+ }
+
+ /**
+ * Get a copy of the current set of repository change listeners
+ * <p>
+ * The array has no references, so is safe for iteration and modification
+ *
+ * @return a copy of the current repository change listeners
+ */
+ private static synchronized RepositoryChangeListener[] getRepositoryChangeListeners() {
+ return repositoryChangeListeners
+ .toArray(new RepositoryChangeListener[repositoryChangeListeners
+ .size()]);
+ }
+
+ /**
+ * @param p
+ * @return {@link GitProjectData} for the specified project
+ */
+ public synchronized static GitProjectData get(final IProject p) {
+ try {
+ GitProjectData d = lookup(p);
+ if (d == null
+ && RepositoryProvider.getProvider(p) instanceof GitProvider) {
+ d = new GitProjectData(p).load();
+ cache(p, d);
+ }
+ return d;
+ } catch (IOException err) {
+ Activator.logError(CoreText.GitProjectData_missing, err);
+ return null;
+ }
+ }
+
+ /**
+ * Drop the Eclipse project from our association of projects/repositories
+ *
+ * @param p Eclipse project
+ */
+ public static void delete(final IProject p) {
+ trace("delete(" + p.getName() + ")");
+ GitProjectData d = lookup(p);
+ if (d == null) {
+ try {
+ d = new GitProjectData(p).load();
+ } catch (IOException ioe) {
+ d = new GitProjectData(p);
+ }
+ }
+ d.delete();
+ }
+
+ static void trace(final String m) {
+ Activator.trace("(GitProjectData) " + m);
+ }
+
+ private synchronized static void cache(final IProject p,
+ final GitProjectData d) {
+ projectDataCache.put(p, d);
+ }
+
+ private synchronized static void uncache(final IProject p) {
+ if (projectDataCache.remove(p) != null) {
+ trace("uncacheDataFor(" + p.getName() + ")");
+ }
+ }
+
+ private synchronized static GitProjectData lookup(final IProject p) {
+ return projectDataCache.get(p);
+ }
+
+ private synchronized static Repository lookupRepository(final File gitDir)
+ throws IOException {
+ final Iterator i = repositoryCache.entrySet().iterator();
+ while (i.hasNext()) {
+ final Map.Entry e = (Map.Entry) i.next();
+ if (((Reference) e.getValue()).get() == null) {
+ i.remove();
+ }
+ }
+
+ final Reference r = repositoryCache.get(gitDir);
+ Repository d = r != null ? (Repository) r.get() : null;
+ if (d == null) {
+ d = new Repository(gitDir);
+ repositoryCache.put(gitDir, new WeakReference<Repository>(d));
+ }
+ return d;
+ }
+
+ /**
+ * Update the settings for the global window cache of the workspace.
+ */
+ public static void reconfigureWindowCache() {
+ final WindowCacheConfig c = new WindowCacheConfig();
+ Preferences p = Activator.getDefault().getPluginPreferences();
+ c.setPackedGitLimit(p.getInt(GitCorePreferences.core_packedGitLimit));
+ c.setPackedGitWindowSize(p.getInt(GitCorePreferences.core_packedGitWindowSize));
+ c.setPackedGitMMAP(p.getBoolean(GitCorePreferences.core_packedGitMMAP));
+ c.setDeltaBaseCacheLimit(p.getInt(GitCorePreferences.core_deltaBaseCacheLimit));
+ WindowCache.reconfigure(c);
+ }
+
+ private final IProject project;
+
+ private final Collection<RepositoryMapping> mappings = new ArrayList<RepositoryMapping>();
+
+ private final Set<IResource> protectedResources = new HashSet<IResource>();
+
+ /**
+ * Construct a {@link GitProjectData} for the mapping
+ * of a project.
+ *
+ * @param p Eclipse project
+ */
+ public GitProjectData(final IProject p) {
+ project = p;
+ }
+
+ /**
+ * @return the Eclipse project mapped through this resource.
+ */
+ public IProject getProject() {
+ return project;
+ }
+
+ /**
+ * TODO: is this right?
+ *
+ * @param newMappings
+ */
+ public void setRepositoryMappings(final Collection<RepositoryMapping> newMappings) {
+ mappings.clear();
+ mappings.addAll(newMappings);
+ remapAll();
+ }
+
+ /**
+ * Hide our private parts from the navigators other browsers.
+ *
+ * @throws CoreException
+ */
+ public void markTeamPrivateResources() throws CoreException {
+ for (final Object rmObj : mappings) {
+ final RepositoryMapping rm = (RepositoryMapping)rmObj;
+ final IContainer c = rm.getContainer();
+ if (c == null)
+ continue; // Not fully mapped yet?
+
+ final IResource dotGit = c.findMember(".git");
+ if (dotGit != null) {
+ try {
+ final Repository r = rm.getRepository();
+ final File dotGitDir = dotGit.getLocation().toFile()
+ .getCanonicalFile();
+ if (dotGitDir.equals(r.getDirectory())) {
+ trace("teamPrivate " + dotGit);
+ dotGit.setTeamPrivateMember(true);
+ }
+ } catch (IOException err) {
+ throw Activator.error(CoreText.Error_CanonicalFile, err);
+ }
+ }
+ }
+ }
+
+ /**
+ * @param f
+ * @return true if a resource is protected in this repository
+ */
+ public boolean isProtected(final IResource f) {
+ return protectedResources.contains(f);
+ }
+
+ /**
+ * @param r any workbench resource contained within this project.
+ * @return the mapping for the specified project
+ */
+ public RepositoryMapping getRepositoryMapping(IResource r) {
+ try {
+ for (; r != null; r = r.getParent()) {
+ final RepositoryMapping m;
+
+ if (!r.isAccessible())
+ continue;
+ m = (RepositoryMapping) r.getSessionProperty(MAPPING_KEY);
+ if (m != null)
+ return m;
+ }
+ } catch (CoreException err) {
+ Activator.logError("Failed finding RepositoryMapping", err);
+ }
+ return null;
+ }
+
+ private void delete() {
+ final File dir = propertyFile().getParentFile();
+ final File[] todel = dir.listFiles();
+ if (todel != null) {
+ for (int k = 0; k < todel.length; k++) {
+ if (todel[k].isFile()) {
+ todel[k].delete();
+ }
+ }
+ }
+ dir.delete();
+ trace("deleteDataFor(" + getProject().getName() + ")");
+ uncache(getProject());
+ }
+
+ /**
+ * Store information about the repository connection in the workspace
+ *
+ * @throws CoreException
+ */
+ public void store() throws CoreException {
+ final File dat = propertyFile();
+ final File tmp;
+ boolean ok = false;
+
+ try {
+ trace("save " + dat);
+ tmp = File.createTempFile("gpd_", ".prop", dat.getParentFile());
+ final FileOutputStream o = new FileOutputStream(tmp);
+ try {
+ final Properties p = new Properties();
+ final Iterator i = mappings.iterator();
+ while (i.hasNext()) {
+ ((RepositoryMapping) i.next()).store(p);
+ }
+ p.store(o, "GitProjectData");
+ ok = true;
+ } finally {
+ o.close();
+ if (!ok) {
+ tmp.delete();
+ }
+ }
+ } catch (IOException ioe) {
+ throw Activator.error(NLS.bind(CoreText.GitProjectData_saveFailed,
+ dat), ioe);
+ }
+
+ dat.delete();
+ if (!tmp.renameTo(dat)) {
+ tmp.delete();
+ throw Activator.error(NLS.bind(CoreText.GitProjectData_saveFailed,
+ dat), null);
+ }
+ }
+
+ private File propertyFile() {
+ return new File(getProject()
+ .getWorkingLocation(Activator.getPluginId()).toFile(),
+ "GitProjectData.properties");
+ }
+
+ private GitProjectData load() throws IOException {
+ final File dat = propertyFile();
+ trace("load " + dat);
+
+ final FileInputStream o = new FileInputStream(dat);
+ try {
+ final Properties p = new Properties();
+ p.load(o);
+
+ mappings.clear();
+ final Iterator keyItr = p.keySet().iterator();
+ while (keyItr.hasNext()) {
+ final String key = keyItr.next().toString();
+ if (RepositoryMapping.isInitialKey(key)) {
+ mappings.add(new RepositoryMapping(p, key));
+ }
+ }
+ } finally {
+ o.close();
+ }
+
+ remapAll();
+ return this;
+ }
+
+ private void remapAll() {
+ protectedResources.clear();
+ final Iterator i = mappings.iterator();
+ while (i.hasNext()) {
+ map((RepositoryMapping) i.next());
+ }
+ }
+
+ private void map(final RepositoryMapping m) {
+ final IResource r;
+ final File git;
+ final IResource dotGit;
+ IContainer c = null;
+
+ m.clear();
+ r = getProject().findMember(m.getContainerPath());
+ if (r instanceof IContainer) {
+ c = (IContainer) r;
+ } else {
+ c = (IContainer) r.getAdapter(IContainer.class);
+ }
+
+ if (c == null) {
+ Activator.logError(CoreText.GitProjectData_mappedResourceGone,
+ new FileNotFoundException(m.getContainerPath().toString()));
+ m.clear();
+ return;
+ }
+ m.setContainer(c);
+
+ git = c.getLocation().append(m.getGitDirPath()).toFile();
+ if (!git.isDirectory() || !new File(git, "config").isFile()) {
+ Activator.logError(CoreText.GitProjectData_mappedResourceGone,
+ new FileNotFoundException(m.getContainerPath().toString()));
+ m.clear();
+ return;
+ }
+
+ try {
+ m.setRepository(lookupRepository(git));
+ } catch (IOException ioe) {
+ Activator.logError(CoreText.GitProjectData_mappedResourceGone,
+ new FileNotFoundException(m.getContainerPath().toString()));
+ m.clear();
+ return;
+ }
+
+ m.fireRepositoryChanged();
+
+ trace("map " + c + " -> " + m.getRepository());
+ try {
+ c.setSessionProperty(MAPPING_KEY, m);
+ } catch (CoreException err) {
+ Activator.logError("Failed to cache RepositoryMapping", err);
+ }
+
+ dotGit = c.findMember(".git");
+ if (dotGit != null && dotGit.getLocation().toFile().equals(git)) {
+ protect(dotGit);
+ }
+ }
+
+ private void protect(IResource c) {
+ while (c != null && !c.equals(getProject())) {
+ trace("protect " + c);
+ protectedResources.add(c);
+ c = c.getParent();
+ }
+ }
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/project/RepositoryChangeListener.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/project/RepositoryChangeListener.java
new file mode 100644
index 0000000000..401400a5ec
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/project/RepositoryChangeListener.java
@@ -0,0 +1,28 @@
+/*******************************************************************************
+ * Copyright (C) 2007, Shawn O. Pearce <spearce@spearce.org>
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.egit.core.project;
+
+/**
+ * Receives notification of a repository change event.
+ * <p>
+ * A change listener may be called from any thread, especially background job
+ * threads, but also from the UI thread. Implementors are encouraged to complete
+ * quickly, and make arrange for their tasks to run on the UI event thread if
+ * necessary.
+ * </p>
+ */
+public interface RepositoryChangeListener {
+ /**
+ * Invoked when a repository has had some or all of its contents change.
+ *
+ * @param which
+ * the affected repository. Never null.
+ */
+ public void repositoryChanged(RepositoryMapping which);
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/project/RepositoryFinder.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/project/RepositoryFinder.java
new file mode 100644
index 0000000000..fbd9addc3e
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/project/RepositoryFinder.java
@@ -0,0 +1,143 @@
+/*******************************************************************************
+ * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2007, Shawn O. Pearce <spearce@spearce.org>
+ * Copyright (C) 2008, Google Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.egit.core.project;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.SubProgressMonitor;
+import org.eclipse.egit.core.CoreText;
+
+/**
+ * Searches for existing Git repositories associated with a project's files.
+ * <p>
+ * This finder algorithm searches a project's contained files to see if any of
+ * them are located within the working directory of an existing Git repository.
+ * The finder searches through linked resources, as the EGit core is capable of
+ * dealing with linked directories spanning multiple repositories in the same
+ * project.
+ * </p>
+ * <p>
+ * The search algorithm is exhaustive, it will find all matching repositories.
+ * For the project itself as well as for each linked container within the
+ * project it scans down the local filesystem trees to locate any Git
+ * repositories which may be found there. It also scans up the local filesystem
+ * tree to locate any Git repository which may be outside of Eclipse's
+ * workspace-view of the world, but which contains the project or a linked
+ * resource within the project. In short, if there is a Git repository
+ * associated, it finds it.
+ * </p>
+ */
+public class RepositoryFinder {
+ private final IProject proj;
+
+ private final Collection<RepositoryMapping> results = new ArrayList<RepositoryMapping>();
+
+ /**
+ * Create a new finder to locate Git repositories for a project.
+ *
+ * @param p
+ * the project this new finder should locate the existing Git
+ * repositories of.
+ */
+ public RepositoryFinder(final IProject p) {
+ proj = p;
+ }
+
+ /**
+ * Run the search algorithm.
+ *
+ * @param m
+ * a progress monitor to report feedback to; may be null.
+ * @return all found {@link RepositoryMapping} instances associated with the
+ * project supplied to this instance's constructor.
+ * @throws CoreException
+ * Eclipse was unable to access its workspace, and threw up on
+ * us. We're throwing it back at the caller.
+ */
+ public Collection<RepositoryMapping> find(IProgressMonitor m) throws CoreException {
+ if (m == null) {
+ m = new NullProgressMonitor();
+ }
+ find(m, proj);
+ return results;
+ }
+
+ private void find(final IProgressMonitor m, final IContainer c)
+ throws CoreException {
+ final IPath loc = c.getLocation();
+
+ m.beginTask("", 101);
+ m.subTask(CoreText.RepositoryFinder_finding);
+ try {
+ if (loc != null) {
+ final File fsLoc = loc.toFile();
+ final File ownCfg = configFor(fsLoc);
+ final IResource[] children;
+
+ if (ownCfg.isFile()) {
+ register(c, ownCfg.getParentFile());
+ } else if (c.isLinked() || c instanceof IProject) {
+ File p = fsLoc.getParentFile();
+ while (p != null) {
+ final File pCfg = configFor(p);
+ if (pCfg.isFile()) {
+ register(c, pCfg.getParentFile());
+ break;
+ }
+ p = p.getParentFile();
+ }
+ }
+ m.worked(1);
+
+ children = c.members();
+ if (children != null && children.length > 0) {
+ final int scale = 100 / children.length;
+ for (int k = 0; k < children.length; k++) {
+ final IResource o = children[k];
+ if (o instanceof IContainer
+ && !o.getName().equals(".git")) {
+ find(new SubProgressMonitor(m, scale),
+ (IContainer) o);
+ } else {
+ m.worked(scale);
+ }
+ }
+ }
+ }
+ } finally {
+ m.done();
+ }
+ }
+
+ private File configFor(final File fsLoc) {
+ return new File(new File(fsLoc, ".git"), "config");
+ }
+
+ private void register(final IContainer c, final File gitdir) {
+ File f;
+ try {
+ f = gitdir.getCanonicalFile();
+ } catch (IOException ioe) {
+ f = gitdir.getAbsoluteFile();
+ }
+ results.add(new RepositoryMapping(c, f));
+ }
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/project/RepositoryMapping.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/project/RepositoryMapping.java
new file mode 100644
index 0000000000..18238cc834
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/project/RepositoryMapping.java
@@ -0,0 +1,248 @@
+/*******************************************************************************
+ * Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
+ * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
+ * Copyright (C) 2008, Shunichi Fuji <palglowr@gmail.com>
+ * Copyright (C) 2008, Google Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.egit.core.project;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.util.Properties;
+
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.egit.core.GitProvider;
+import org.eclipse.team.core.RepositoryProvider;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.GitIndex;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.Tree;
+import org.eclipse.jgit.lib.TreeEntry;
+import org.eclipse.jgit.lib.GitIndex.Entry;
+
+/**
+ * This class keeps track
+ */
+public class RepositoryMapping {
+ static boolean isInitialKey(final String key) {
+ return key.endsWith(".gitdir");
+ }
+
+ private final String containerPath;
+
+ private final String gitdirPath;
+
+ private Repository db;
+
+ private String workdirPrefix;
+
+ private IContainer container;
+
+ /**
+ * Construct a {@link RepositoryMapping} for a previously connected project.
+ *
+ * @param p TODO
+ * @param initialKey TODO
+ */
+ public RepositoryMapping(final Properties p, final String initialKey) {
+ final int dot = initialKey.lastIndexOf('.');
+
+ containerPath = initialKey.substring(0, dot);
+ gitdirPath = p.getProperty(initialKey);
+ }
+
+ /**
+ * Construct a {@link RepositoryMapping} for previously
+ * unknown project.
+ *
+ * @param mappedContainer
+ * @param gitDir
+ */
+ public RepositoryMapping(final IContainer mappedContainer, final File gitDir) {
+ final IPath cLoc = mappedContainer.getLocation()
+ .removeTrailingSeparator();
+ final IPath gLoc = Path.fromOSString(gitDir.getAbsolutePath())
+ .removeTrailingSeparator();
+ final IPath gLocParent = gLoc.removeLastSegments(1);
+ String p;
+ int cnt;
+
+ container = mappedContainer;
+ containerPath = container.getProjectRelativePath().toPortableString();
+
+ if (cLoc.isPrefixOf(gLoc)) {
+ gitdirPath = gLoc.removeFirstSegments(
+ gLoc.matchingFirstSegments(cLoc)).toPortableString();
+ } else if (gLocParent.isPrefixOf(cLoc)) {
+ cnt = cLoc.segmentCount() - cLoc.matchingFirstSegments(gLocParent);
+ p = "";
+ while (cnt-- > 0) {
+ p += "../";
+ }
+ p += gLoc.segment(gLoc.segmentCount() - 1);
+ gitdirPath = p;
+ } else {
+ gitdirPath = gLoc.toPortableString();
+ }
+ }
+
+ IPath getContainerPath() {
+ return Path.fromPortableString(containerPath);
+ }
+
+ IPath getGitDirPath() {
+ return Path.fromPortableString(gitdirPath);
+ }
+
+ /**
+ * @return the workdir file, i.e. where the files are checked out
+ */
+ public File getWorkDir() {
+ return getRepository().getWorkDir();
+ }
+
+ synchronized void clear() {
+ db = null;
+ workdirPrefix = null;
+ container = null;
+ }
+
+ /**
+ * @return a reference to the repository object handled by this mapping
+ */
+ public synchronized Repository getRepository() {
+ return db;
+ }
+
+ synchronized void setRepository(final Repository r) {
+ db = r;
+
+ try {
+ workdirPrefix = getWorkDir().getCanonicalPath();
+ } catch (IOException err) {
+ workdirPrefix = getWorkDir().getAbsolutePath();
+ }
+ workdirPrefix = workdirPrefix.replace('\\', '/');
+ if (!workdirPrefix.endsWith("/"))
+ workdirPrefix += "/";
+ }
+
+ /**
+ * @return the mapped container (currently project)
+ */
+ public synchronized IContainer getContainer() {
+ return container;
+ }
+
+ synchronized void setContainer(final IContainer c) {
+ container = c;
+ }
+
+ /**
+ * Notify registered {@link RepositoryChangeListener}s of a change.
+ *
+ * @see GitProjectData#addRepositoryChangeListener(RepositoryChangeListener)
+ */
+ public void fireRepositoryChanged() {
+ GitProjectData.fireRepositoryChanged(this);
+ }
+
+ synchronized void store(final Properties p) {
+ p.setProperty(containerPath + ".gitdir", gitdirPath);
+ }
+
+ public String toString() {
+ return "RepositoryMapping[" + containerPath + " -> " + gitdirPath + "]";
+ }
+
+ /**
+ * Check whether a resource has been changed relative to the checked out
+ * version. Content is assumed changed by this routine if the resource's
+ * modification time differs from what is recorded in the index, but the
+ * real content hasn't changed. The reason is performance.
+ *
+ * @param rsrc
+ * @return true if a resource differs in the workdir or index relative to
+ * HEAD
+ *
+ * @throws IOException
+ * @throws UnsupportedEncodingException
+ */
+ public boolean isResourceChanged(IResource rsrc) throws IOException, UnsupportedEncodingException {
+ Repository repository = getRepository();
+ GitIndex index = repository.getIndex();
+ String repoRelativePath = getRepoRelativePath(rsrc);
+ Tree headTree = repository.mapTree(Constants.HEAD);
+ TreeEntry blob = headTree!=null ? headTree.findBlobMember(repoRelativePath) : null;
+ Entry entry = index.getEntry(repoRelativePath);
+ if (rsrc instanceof IFile && entry == null && blob == null)
+ return false;
+ if (entry == null)
+ return true; // flags new resources as changes
+ if (blob == null)
+ return true; // added in index
+ boolean hashesDiffer = !entry.getObjectId().equals(blob.getId());
+ return hashesDiffer || entry.isModified(getWorkDir());
+ }
+
+ /**
+ * @param rsrc
+ * @return the path relative to the Git repository, including base name.
+ */
+ public String getRepoRelativePath(final IResource rsrc) {
+ // We should only be called for resources that are actually
+ // in this repository, so we can safely assume that their
+ // path prefix matches workdirPrefix. Testing that here is
+ // rather expensive so we don't bother.
+ //
+ final int pfxLen = workdirPrefix.length();
+ final String p = rsrc.getLocation().toString();
+ final int pLen = p.length();
+ if (pLen > pfxLen)
+ return p.substring(pfxLen);
+ else if (p.length() == pfxLen - 1)
+ return "";
+ return null;
+ }
+
+ /**
+ * Get the repository mapping for a resource
+ *
+ * @param resource
+ * @return the RepositoryMapping for this resource,
+ * or null for non GitProvider.
+ */
+ public static RepositoryMapping getMapping(final IResource resource) {
+ final IProject project = resource.getProject();
+ if (project == null)
+ return null;
+
+ final RepositoryProvider rp = RepositoryProvider.getProvider(project);
+ if (!(rp instanceof GitProvider))
+ return null;
+
+ if (((GitProvider)rp).getData() == null)
+ return null;
+
+ return ((GitProvider)rp).getData().getRepositoryMapping(resource);
+ }
+
+ /**
+ * @return the name of the .git directory
+ */
+ public String getGitDir() {
+ return gitdirPath;
+ }
+}

Back to the top