diff --git a/lib/git/base.rb b/lib/git/base.rb
index 068d7931..c033a845 100644
--- a/lib/git/base.rb
+++ b/lib/git/base.rb
@@ -58,7 +58,7 @@ def self.init(working_dir, opts = {})
       # Submodules have a .git *file* not a .git folder.
       # This file's contents point to the location of
       # where the git refs are held (In the parent repo)
-      if File.file?('.git')
+      if opts[:working_directory] && File.file?(File.join(opts[:working_directory], '.git'))
         git_file = File.open('.git').read[8..-1].strip
         opts[:repository] = git_file
         opts[:index] = git_file + '/index'
@@ -72,13 +72,24 @@ def self.init(working_dir, opts = {})
     # opens a new Git Project from a working directory
     # you can specify non-standard git_dir and index file in the options
     def self.open(working_dir, opts={})
-      self.new({:working_directory => working_dir}.merge(opts))
+      opts[:working_directory] ||= working_dir
+      opts[:repository] ||= File.join(opts[:working_directory], '.git')
+
+      # Submodules have a .git *file* not a .git folder.
+      # This file's contents point to the location of
+      # where the git refs are held (In the parent repo)
+      if opts[:working_directory] && File.file?(File.join(opts[:working_directory], '.git'))
+        git_file = File.open('.git').read[8..-1].strip
+        opts[:repository] = git_file
+        opts[:index] = git_file + '/index'
+      end
+      self.new(opts)
     end
     
     def initialize(options = {})
       if working_dir = options[:working_directory]
         options[:repository] ||= File.join(working_dir, '.git')
-        options[:index] ||= File.join(working_dir, '.git', 'index')
+        options[:index] ||= File.join(options[:repository], 'index')
       end
       if options[:log]
         @logger = options[:log]
diff --git a/tests/test_helper.rb b/tests/test_helper.rb
index 617d8c83..e3729b0e 100644
--- a/tests/test_helper.rb
+++ b/tests/test_helper.rb
@@ -26,7 +26,7 @@ def set_file_paths
   
   teardown
   def git_teardown
-    if @tmp_path
+    if instance_variable_defined?(:@tmp_path)
       FileUtils.rm_r(@tmp_path)
     end
   end
diff --git a/tests/units/test_git_dir.rb b/tests/units/test_git_dir.rb
new file mode 100644
index 00000000..78d7c053
--- /dev/null
+++ b/tests/units/test_git_dir.rb
@@ -0,0 +1,73 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../test_helper'
+
+class TestGitDir < Test::Unit::TestCase
+  def test_index_calculated_from_git_dir
+    Dir.mktmpdir do |work_tree|
+      Dir.mktmpdir do |git_dir|
+        git = Git.open(work_tree, repository: git_dir)
+
+        assert_equal(work_tree, git.dir.path)
+        assert_equal(git_dir, git.repo.path)
+
+        # Since :index was not given in the options to Git#open, index should
+        # be defined automatically based on the git_dir.
+        #
+        index = File.join(git_dir, 'index')
+        assert_equal(index, git.index.path)
+      end
+    end
+  end
+
+  # Test the case where the git-dir is not a subdirectory of work-tree
+  #
+  def test_git_dir_outside_work_tree
+    Dir.mktmpdir do |work_tree|
+      Dir.mktmpdir do |git_dir|
+        # Setup a bare repository
+        #
+        source_git_dir = File.expand_path(File.join('tests', 'files', 'working.git'))
+        FileUtils.cp_r(Dir["#{source_git_dir}/*"], git_dir, preserve: true)
+        git = Git.open(work_tree, repository: git_dir)
+
+        assert_equal(work_tree, git.dir.path)
+        assert_equal(git_dir, git.repo.path)
+
+        # Reconstitute the work tree from the bare repository
+        #
+        branch = 'master'
+        git.checkout(branch, force: true)
+
+        # Make sure the work tree contains the expected files
+        #
+        expected_files = %w[ex_dir example.txt]
+        actual_files = Dir[File.join(work_tree, '*')].map { |f| File.basename(f) }
+        assert_equal(expected_files, actual_files)
+
+        # None of the expected files should have a status that says it has been changed
+        #
+        expected_files.each do |file|
+          assert_equal(false, git.status.changed?(file))
+        end
+
+        # Change a file and make sure it's status says it has been changed
+        #
+        file = 'example.txt'
+        File.open(File.join(work_tree, file), "a") { |f| f.write("A new line") }
+        assert_equal(true, git.status.changed?(file))
+
+        # Add and commit the file and then check that:
+        # * the file is not flagged as changed anymore
+        # * the commit was added to the log
+        #
+        max_log_size = 100
+        assert_equal(64, git.log(max_log_size).size)
+        git.add(file)
+        git.commit('This is a new commit')
+        assert_equal(false, git.status.changed?(file))
+        assert_equal(65, git.log(max_log_size).size)
+      end
+    end
+  end
+end