diff --git a/app/helpers/repositories_helper.rb b/app/helpers/repositories_helper.rb
index d7d7f43..5b33c85 100644
--- a/app/helpers/repositories_helper.rb
+++ b/app/helpers/repositories_helper.rb
@@ -76,6 +76,10 @@ module RepositoriesHelper
       content_tag('p', form.text_field(:url, :label => 'Root directory', :size => 60, :required => true, :disabled => (repository && !repository.root_url.blank?)))
   end
 
+  def git_field_tags(form, repository)
+      content_tag('p', form.text_field(:url, :label => 'Root directory', :size => 60, :required => true, :disabled => (repository && !repository.root_url.blank?)))
+  end
+
   def cvs_field_tags(form, repository)
       content_tag('p', form.text_field(:root_url, :label => 'CVSROOT', :size => 60, :required => true, :disabled => !repository.new_record?)) +
       content_tag('p', form.text_field(:url, :label => 'Module', :size => 30, :required => true, :disabled => !repository.new_record?))
diff --git a/app/models/repository/git.rb b/app/models/repository/git.rb
new file mode 100644
index 0000000..9ed06d9
--- /dev/null
+++ b/app/models/repository/git.rb
@@ -0,0 +1,97 @@
+# redMine - project management software
+# Copyright (C) 2006-2007  Jean-Philippe Lang
+# Copyright (C) 2007  Patrick Aljord patcito@ŋmail.com
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+require 'redmine/scm/adapters/git_adapter'
+
+class Repository::Git < Repository
+  attr_protected :root_url
+  validates_presence_of :url
+
+  def scm_adapter
+    Redmine::Scm::Adapters::GitAdapter
+  end
+  
+  def self.scm_name
+    'Git'
+  end
+  
+  def entries(path=nil, identifier=nil)
+    entries=scm.entries(path, identifier)
+    if entries
+      entries.each do |entry|
+        next unless entry.is_file?
+        # Search the DB for the entry's last change
+        change = changes.find(:first, :conditions => ["path = ?", scm.with_leading_slash(entry.path)], :order => "#{Changeset.table_name}.committed_on DESC")
+        if change
+          entry.lastrev.identifier = change.changeset.revision
+          entry.lastrev.name = change.changeset.revision
+          entry.lastrev.author = change.changeset.committer
+          entry.lastrev.revision = change.revision
+        end
+      end
+    end
+    entries
+  end
+
+  def changesets_for_path(path)
+    path = "#{path}" unless path.starts_with?('/')
+    Change.find(:all, :include => :changeset, 
+                :conditions => ["repository_id = ? AND path = ?", id, path],
+                :order => "committed_on DESC, #{Changeset.table_name}.revision DESC").collect(&:changeset)
+  end
+
+  def fetch_changesets
+    scm_info = scm.info
+    
+
+    puts "fetch_changesets"
+    if scm_info
+      # latest revision found in database
+      db_revision = latest_changeset ? latest_changeset.scmid : nil
+      puts db_revision.to_s
+      next_rev = latest_changeset ? latest_changeset.revision + 1 : 1
+      puts next_rev.to_s
+      # latest revision in the repository
+      scm_revision = scm_info.lastrev.scmid
+      puts scm_revision.to_s
+
+      unless changesets.find_by_scmid(scm_revision)
+
+        revisions = scm.revisions('', db_revision, nil)
+        transaction do
+          revisions.reverse_each do |revision|
+            changeset = Changeset.create(:repository => self,
+                                         :revision => next_rev,
+                                         :scmid => revision.scmid,
+                                         :committer => revision.author, 
+                                         :committed_on => revision.time,
+                                         :comments => revision.message)
+            next_rev += 1
+            
+            revision.paths.each do |change|
+              Change.create(:changeset => changeset,
+                            :action => change[:action],
+                            :path => change[:path],
+                            :from_path => change[:from_path],
+                            :from_revision => change[:from_revision])
+            end
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/app/views/repositories/_dir_list_content.rhtml b/app/views/repositories/_dir_list_content.rhtml
index 3525752..0d955be 100644
--- a/app/views/repositories/_dir_list_content.rhtml
+++ b/app/views/repositories/_dir_list_content.rhtml
@@ -25,7 +25,7 @@ end %>
 <td class="size"><%= (entry.size ? number_to_human_size(entry.size) : "?") unless entry.is_dir? %></td>
 <td class="revision"><%= link_to(entry.lastrev.name, :action => 'revision', :id => @project, :rev => entry.lastrev.identifier) if entry.lastrev && entry.lastrev.identifier %></td>
 <td class="age"><%= distance_of_time_in_words(entry.lastrev.time, Time.now) if entry.lastrev && entry.lastrev.time %></td>
-<td class="author"><%=h(entry.lastrev.author) if entry.lastrev %></td>
+<td class="author"><%=h(entry.lastrev.author.split('<').first) if entry.lastrev %></td>
 <% changeset = @project.repository.changesets.find_by_revision(entry.lastrev.identifier) if entry.lastrev %>
 <td class="comments"><%=h truncate(changeset.comments, 50) unless changeset.nil? %></td>
 </tr>
diff --git a/app/views/repositories/_revisions.rhtml b/app/views/repositories/_revisions.rhtml
index ea72232..e5a08d7 100644
--- a/app/views/repositories/_revisions.rhtml
+++ b/app/views/repositories/_revisions.rhtml
@@ -17,7 +17,7 @@
 <td class="checkbox"><%= radio_button_tag('rev', changeset.revision, (line_num==1), :id => "cb-#{line_num}", :onclick => "$('cbto-#{line_num+1}').checked=true;") if show_diff && (line_num < revisions.size) %></td>
 <td class="checkbox"><%= radio_button_tag('rev_to', changeset.revision, (line_num==2), :id => "cbto-#{line_num}", :onclick => "if ($('cb-#{line_num}').checked==true) {$('cb-#{line_num-1}').checked=true;}") if show_diff && (line_num > 1) %></td>
 <td class="committed_on"><%= format_time(changeset.committed_on) %></td>
-<td class="author"><%=h changeset.committer %></td>
+<td class="author"><%=h changeset.committer.split('<').first %></td>
 <td class="comments"><%= textilizable(changeset.comments) %></td>
 </tr>
 <% line_num += 1 %>
diff --git a/doc/RUNNING_TESTS b/doc/RUNNING_TESTS
index fde2441..7a5e2b9 100644
--- a/doc/RUNNING_TESTS
+++ b/doc/RUNNING_TESTS
@@ -19,3 +19,19 @@ gunzip < test/fixtures/repositories/bazaar_repository.tar.gz | tar -xv -C tmp/te
 Mercurial
 ---------
 gunzip < test/fixtures/repositories/mercurial_repository.tar.gz | tar -xv -C tmp/test
+
+Git
+---
+gunzip < test/fixtures/repositories/git_repository.tar.gz | tar -xv -C tmp/test
+
+
+Running Tests
+=============
+
+Run 
+
+  rake --tasks | grep test
+
+to see available tests.
+
+RAILS_ENV=test rake test will run tests.
diff --git a/lib/redmine.rb b/lib/redmine.rb
index e76d77e..4c5cbda 100644
--- a/lib/redmine.rb
+++ b/lib/redmine.rb
@@ -10,7 +10,7 @@ rescue LoadError
   # RMagick is not available
 end
 
-REDMINE_SUPPORTED_SCM = %w( Subversion Darcs Mercurial Cvs Bazaar )
+REDMINE_SUPPORTED_SCM = %w( Subversion Darcs Mercurial Cvs Bazaar Git )
 
 # Permissions
 Redmine::AccessControl.map do |map|
diff --git a/lib/redmine/scm/adapters/abstract_adapter.rb b/lib/redmine/scm/adapters/abstract_adapter.rb
index 5db4964..2410a43 100644
--- a/lib/redmine/scm/adapters/abstract_adapter.rb
+++ b/lib/redmine/scm/adapters/abstract_adapter.rb
@@ -183,7 +183,7 @@ module Redmine
           }.last
         end 
       end
-      
+
       class Revision
         attr_accessor :identifier, :scmid, :name, :author, :time, :message, :paths, :revision, :branch
         def initialize(attributes={})
diff --git a/lib/redmine/scm/adapters/git_adapter.rb b/lib/redmine/scm/adapters/git_adapter.rb
new file mode 100644
index 0000000..edc7c77
--- /dev/null
+++ b/lib/redmine/scm/adapters/git_adapter.rb
@@ -0,0 +1,315 @@
+# redMine - project management software
+# Copyright (C) 2006-2007  Jean-Philippe Lang
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+require 'redmine/scm/adapters/abstract_adapter'
+
+module Redmine
+  module Scm
+    module Adapters    
+      class GitAdapter < AbstractAdapter
+        
+        # Git executable name
+        GIT_BIN = "git"
+
+        # Convert an identifier to a git revision
+        def id_to_rev(identifier)
+          if identifier.nil?
+            return nil
+          end
+          
+          cmd = "cd #{target('')} && #{GIT_BIN} log --reverse --raw "
+          cmd << "--skip="
+          cmd << ((identifier - 1).to_s)
+          answer = nil
+
+          shellout(cmd) do |io|
+            
+            io.each_line do |line|
+              if answer.nil? && line =~ /^commit ([0-9a-f]{40})$/
+                answer = $1
+              else
+                next
+              end
+            end
+          end
+
+          return answer
+        end
+
+        #get the revision of a particuliar file
+	def get_rev (rev,path)
+          cmd="cd #{target('')} && git show #{rev} #{path}" if rev!='latest'
+          cmd="cd #{target('')} && git log -1 master -- #{path}" if 
+            rev=='latest' or rev.nil?
+          rev=[]
+          i=0
+          puts "get_rev"
+          puts cmd
+          shellout(cmd) do |io|
+            files=[]
+            changeset = {}
+            parsing_descr = 0  #0: not parsing desc or files, 1: parsing desc, 2: parsing files
+            line_feeds = 0
+
+            io.each_line do |line|
+              if line =~ /^commit ([0-9a-f]{40})$/
+                key = "commit"
+                value = $1
+                if (parsing_descr == 1 || parsing_descr == 2)
+                  parsing_descr = 0
+                  rev = Revision.new({:identifier => nil,
+                                             :scmid => changeset[:commit],
+                                             :author => changeset[:author],
+                                             :time => Time.parse(changeset[:date]),
+                                             :message => changeset[:description],
+                                             :paths => files
+                                            })
+                  changeset = {}
+                  files = []
+                end
+                changeset[:commit] = $1
+              elsif (parsing_descr == 0) && line =~ /^(\w+):\s*(.*)$/
+                key = $1
+                value = $2
+                if key == "Author"
+                  changeset[:author] = value
+                elsif key == "Date"
+                  changeset[:date] = value
+                end
+              elsif (parsing_descr == 0) && line.chomp.to_s == ""
+                parsing_descr = 1
+                changeset[:description] = ""
+              elsif (parsing_descr == 1 || parsing_descr == 2) && line =~ /^:\d+\s+\d+\s+[0-9a-f.]+\s+[0-9a-f.]+\s+(\w)\s+(.+)$/
+                parsing_descr = 2
+                fileaction = $1
+                filepath = $2
+                files << {:action => fileaction, :path => filepath}
+              elsif (parsing_descr == 1) && line.chomp.to_s == ""
+                parsing_descr = 2
+              elsif (parsing_descr == 1)
+                changeset[:description] << line
+              end
+            end	
+            rev = Revision.new({:identifier => nil,
+                                       :scmid => changeset[:commit],
+                                       :author => changeset[:author],
+                                       :time => Time.parse(changeset[:date]),
+                                       :message => changeset[:description],
+                                       :paths => files
+                                      })
+
+          end
+
+          get_rev('latest',path) if rev == []
+
+          return nil if $? && $?.exitstatus != 0
+          return rev
+          #         rescue Errno::ENOENT => e
+          #           raise CommandFailed
+        end
+
+
+        def info
+          #           cmd = "#{GIT_BIN} -R #{target('')} root"
+          #           root_url = nil
+          #           shellout(cmd) do |io|
+          root_url = target('')
+          #           end
+          info = Info.new({:root_url => target(''),
+                            :lastrev => revisions(root_url,nil,nil,nil).first
+                          })
+          info
+        rescue Errno::ENOENT => e
+          return nil
+        end
+        
+        def entries(path=nil, identifier=nil)
+          puts " ENTRIES "
+          print path
+          puts ""
+          print identifier
+          puts ""
+          path ||= ''
+          entries = Entries.new
+          cmd = "cd #{target('')} && #{GIT_BIN} show HEAD:#{path}" if identifier.nil?
+          cmd = "cd #{target('')} && #{GIT_BIN} show #{identifier}:#{path}" if identifier
+          shellout(cmd)  do |io|
+            io.each_line do |line|
+              e = line.chomp.split('\\')
+              unless e.to_s.strip=='' or line[0..3]=='tree'
+                name=e.first.split('/')[0]
+                entries << Entry.new({:name => name,
+                                       :path => (path.empty? ? name : "#{path}/#{name}"),
+                                       :kind => ((e.first.include? '/') ? 'dir' : 'file'),
+                                       :lastrev => get_rev(identifier,(path.empty? ? name : "#{path}/#{name}"))
+                                     }) unless entries.detect{|entry| entry.name == name}
+              end
+            end
+          end
+          return nil if $? && $?.exitstatus != 0
+          entries.sort_by_name
+          #         rescue Errno::ENOENT => e
+          #           raise CommandFailed
+        end
+        
+        def entry(path=nil, identifier=nil)
+          path ||= ''
+          search_path = path.split('/')[0..-2].join('/')
+          entry_name = path.split('/').last
+          e = entries(search_path, identifier)
+          e ? e.detect{|entry| entry.name == entry_name} : nil
+        end
+        
+        def revisions(path, identifier_from, identifier_to, options={})
+          revisions = Revisions.new
+          cmd = "cd #{target('')} && #{GIT_BIN} log --raw "
+          cmd << " #{identifier_from}.. " if identifier_from
+          cmd << " #{identifier_to} " if identifier_to
+          #cmd << " HEAD " if !identifier_to
+          puts "revisions"
+          puts cmd
+          shellout(cmd) do |io|
+            files=[]
+            changeset = {}
+            parsing_descr = 0  #0: not parsing desc or files, 1: parsing desc, 2: parsing files
+            line_feeds = 0
+            revno = 1
+
+            io.each_line do |line|
+              if line =~ /^commit ([0-9a-f]{40})$/
+                key = "commit"
+                value = $1
+                if (parsing_descr == 1 || parsing_descr == 2)
+                  parsing_descr = 0
+                  print revno
+                  puts ""
+                  puts changeset[:description]
+                  revisions << Revision.new({:identifier => nil,
+                                             :scmid => changeset[:commit],
+                                             :author => changeset[:author],
+                                             :time => Time.parse(changeset[:date]),
+                                             :message => changeset[:description],
+                                             :paths => files
+                                            })
+                  changeset = {}
+                  files = []
+                  revno = revno + 1
+                end
+                changeset[:commit] = $1
+              elsif (parsing_descr == 0) && line =~ /^(\w+):\s*(.*)$/
+                key = $1
+                value = $2
+                if key == "Author"
+                  changeset[:author] = value
+                elsif key == "Date"
+                  changeset[:date] = value
+                end
+              elsif (parsing_descr == 0) && line.chomp.to_s == ""
+                parsing_descr = 1
+                changeset[:description] = ""
+              elsif (parsing_descr == 1 || parsing_descr == 2) && line =~ /^:\d+\s+\d+\s+[0-9a-f.]+\s+[0-9a-f.]+\s+(\w)\s+(.+)$/
+                parsing_descr = 2
+                fileaction = $1
+                filepath = $2
+                files << {:action => fileaction, :path => filepath}
+              elsif (parsing_descr == 1) && line.chomp.to_s == ""
+                parsing_descr = 2
+              elsif (parsing_descr == 1)
+                changeset[:description] << line[4..-1]
+              end
+            end	
+            print revno
+            puts ""
+            puts changeset[:description]
+
+            revisions << Revision.new({:identifier => nil,
+                                       :scmid => changeset[:commit],
+                                       :author => changeset[:author],
+                                       :time => Time.parse(changeset[:date]),
+                                       :message => changeset[:description],
+                                       :paths => files
+                                      })
+
+          end
+
+          return nil if $? && $?.exitstatus != 0
+          puts "RETURNING REVISIONS"
+          revisions
+        rescue Errno::ENOENT => e
+          raise CommandFailed
+        end
+        
+        def diff(path, identifier_from, identifier_to=nil, type="inline")
+          path ||= ''
+          if identifier_to
+            identifier_to = identifier_to 
+          else
+            identifier_to = nil
+          end
+
+          puts "calling diff"
+          print identifier_from
+          puts ""
+          print identifier_to
+          puts ""
+          puts "running diff"
+
+          identifier_from = id_to_rev(identifier_from)
+          identifier_to = id_to_rev(identifier_to)
+          
+          cmd = "cd #{target('')} && #{GIT_BIN}  diff   #{identifier_from}^!" if identifier_to.nil?
+          cmd = "cd #{target('')} && #{GIT_BIN}  diff #{identifier_to}  #{identifier_from}" if !identifier_to.nil?
+          cmd << " -- #{path}" unless path.empty?
+          puts cmd
+          diff = []
+          shellout(cmd) do |io|
+            io.each_line do |line|
+              diff << line
+            end
+          end
+          return nil if $? && $?.exitstatus != 0
+          DiffTableList.new diff, type
+          
+        rescue Errno::ENOENT => e
+          raise CommandFailed
+        end
+        
+        def cat(path, identifier=nil)
+          identifier = id_to_rev(identifier)
+          puts " ** CAT "
+          print identifier
+          puts ""
+          if identifier.nil?
+            identifier = 'HEAD'
+          end
+          cmd = "cd #{target('')} && #{GIT_BIN} show #{identifier}:#{path}"
+          cat = nil
+          shellout(cmd) do |io|
+            io.binmode
+            cat = io.read
+          end
+          return nil if $? && $?.exitstatus != 0
+          cat
+        rescue Errno::ENOENT => e
+          raise CommandFailed
+        end
+      end
+    end
+  end
+
+end
+
diff --git a/test/fixtures/repositories/git_repository.tar.gz b/test/fixtures/repositories/git_repository.tar.gz
new file mode 100644
index 0000000..84de88a
Binary files /dev/null and b/test/fixtures/repositories/git_repository.tar.gz differ
diff --git a/test/functional/repositories_git_controller_test.rb b/test/functional/repositories_git_controller_test.rb
new file mode 100644
index 0000000..770147f
--- /dev/null
+++ b/test/functional/repositories_git_controller_test.rb
@@ -0,0 +1,122 @@
+# redMine - project management software
+# Copyright (C) 2006-2007  Jean-Philippe Lang
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+require File.dirname(__FILE__) + '/../test_helper'
+require 'repositories_controller'
+
+# Re-raise errors caught by the controller.
+class RepositoriesController; def rescue_action(e) raise e end; end
+
+class RepositoriesGitControllerTest < Test::Unit::TestCase
+  fixtures :projects, :users, :roles, :members, :repositories, :enabled_modules
+
+  # No '..' in the repository path
+  REPOSITORY_PATH = RAILS_ROOT.gsub(%r{config\/\.\.}, '') + '/tmp/test/git_repository'
+
+  def setup
+    @controller = RepositoriesController.new
+    @request    = ActionController::TestRequest.new
+    @response   = ActionController::TestResponse.new
+    User.current = nil
+    Repository::Git.create(:project => Project.find(3), :url => REPOSITORY_PATH)
+  end
+  
+  if File.directory?(REPOSITORY_PATH)
+    def test_show
+      get :show, :id => 3
+      assert_response :success
+      assert_template 'show'
+      assert_not_nil assigns(:entries)
+      assert_not_nil assigns(:changesets)
+    end
+    
+    def test_browse_root
+      get :browse, :id => 3
+      assert_response :success
+      assert_template 'browse'
+      assert_not_nil assigns(:entries)
+      assert_equal 3, assigns(:entries).size
+      assert assigns(:entries).detect {|e| e.name == 'images' && e.kind == 'dir'}
+      assert assigns(:entries).detect {|e| e.name == 'sources' && e.kind == 'dir'}
+      assert assigns(:entries).detect {|e| e.name == 'README' && e.kind == 'file'}
+    end
+    
+    def test_browse_directory
+      get :browse, :id => 3, :path => ['images']
+      assert_response :success
+      assert_template 'browse'
+      assert_not_nil assigns(:entries)
+      assert_equal 2, assigns(:entries).size
+      entry = assigns(:entries).detect {|e| e.name == 'edit.png'}
+      assert_not_nil entry
+      assert_equal 'file', entry.kind
+      assert_equal 'images/edit.png', entry.path
+    end
+    
+    def test_changes
+      get :changes, :id => 3, :path => ['images', 'edit.png']
+      assert_response :success
+      assert_template 'changes'
+      assert_tag :tag => 'h2', :content => 'edit.png'
+    end
+    
+    def test_entry_show
+      get :entry, :id => 3, :path => ['sources', 'watchers_controller.rb']
+      assert_response :success
+      assert_template 'entry'
+      # Line 19
+      assert_tag :tag => 'th',
+                 :content => /10/,
+                 :attributes => { :class => /line-num/ },
+                 :sibling => { :tag => 'td', :content => /WITHOUT ANY WARRANTY/ }
+    end
+    
+    def test_entry_download
+      get :entry, :id => 3, :path => ['sources', 'watchers_controller.rb'], :format => 'raw'
+      assert_response :success
+      # File content
+      assert @response.body.include?('WITHOUT ANY WARRANTY')
+    end
+  
+    def test_diff
+      # Full diff of changeset 4
+      get :diff, :id => 3, :rev => 4
+      assert_response :success
+      assert_template 'diff'
+      # Line 22 removed
+      assert_tag :tag => 'th',
+                 :content => /22/,
+                 :sibling => { :tag => 'td', 
+                               :attributes => { :class => /diff_out/ },
+                               :content => /def remove/ }
+    end
+    
+    def test_annotate
+      get :annotate, :id => 3, :path => ['sources', 'watchers_controller.rb']
+      assert_response :success
+      assert_template 'annotate'
+      # Line 23, revision 4
+      assert_tag :tag => 'th', :content => /23/,
+                 :sibling => { :tag => 'td', :child => { :tag => 'a', :content => /4/ } },
+                 :sibling => { :tag => 'td', :content => /jsmith/ },
+                 :sibling => { :tag => 'td', :content => /watcher =/ }
+    end
+  else
+    puts "Git test repository NOT FOUND. Skipping functional tests !!!"
+    def test_fake; assert true end
+  end
+end
diff --git a/test/unit/repository_git_test.rb b/test/unit/repository_git_test.rb
new file mode 100644
index 0000000..fdd1c6f
--- /dev/null
+++ b/test/unit/repository_git_test.rb
@@ -0,0 +1,55 @@
+# redMine - project management software
+# Copyright (C) 2006-2007  Jean-Philippe Lang
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+require File.dirname(__FILE__) + '/../test_helper'
+
+class RepositoryGitTest < Test::Unit::TestCase
+  fixtures :projects
+  
+  # No '..' in the repository path
+  REPOSITORY_PATH = RAILS_ROOT.gsub(%r{config\/\.\.}, '') + '/tmp/test/git_repository'
+  
+  def setup
+    @project = Project.find(1)
+    assert @repository = Repository::Git.create(:project => @project, :url => REPOSITORY_PATH)
+  end
+  
+  if File.directory?(REPOSITORY_PATH)  
+    def test_fetch_changesets_from_scratch
+      @repository.fetch_changesets
+      @repository.reload
+      
+      assert_equal 6, @repository.changesets.count
+      assert_equal 11, @repository.changes.count
+      assert_equal "Initial import.\nThe repository contains 3 files.", @repository.changesets.find_by_revision(1).comments
+    end
+    
+    def test_fetch_changesets_incremental
+      @repository.fetch_changesets
+      # Remove changesets with revision > 3
+      @repository.changesets.find(:all, :conditions => 'revision > 3').each(&:destroy)
+      @repository.reload
+      assert_equal 3, @repository.changesets.count
+      
+      @repository.fetch_changesets
+      assert_equal 6, @repository.changesets.count
+    end
+  else
+    puts "Git test repository NOT FOUND. Skipping unit tests !!!"
+    def test_fake; assert true end
+  end
+end
