git: handle worktrees

A worktree checkout has a text file at .git instead of a dir.

BUG=chromium:1104812
TEST=CQ passes

Change-Id: If28efe8943fe8e34d4cd6feb54738ac17926fd3d
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/chromite/+/2551821
Reviewed-by: Stephane Belmon <sbelmon@google.com>
Tested-by: Mike Frysinger <vapier@chromium.org>
Commit-Queue: Mike Frysinger <vapier@chromium.org>
diff --git a/lib/git.py b/lib/git.py
index a6d139e..0f8f939 100644
--- a/lib/git.py
+++ b/lib/git.py
@@ -91,8 +91,18 @@
   Returns:
     Path of the gitdir directory. None if the directory is not a git repo.
   """
-  if os.path.isdir(os.path.join(pwd, '.git')):
-    return os.path.join(pwd, '.git')
+  dotgit = os.path.join(pwd, '.git')
+
+  # A "normal" git checkout.
+  if os.path.isdir(dotgit):
+    return dotgit
+
+  # A git worktree checkout.
+  if os.path.isfile(dotgit):
+    with open(dotgit, 'r') as fp:
+      if fp.read(7) == 'gitdir:':
+        return dotgit
+
   # Is this directory a bare repo with no checkout?
   if os.path.isdir(os.path.join(
       pwd, 'objects')) and os.path.isdir(os.path.join(pwd, 'refs')):
diff --git a/lib/git_unittest.py b/lib/git_unittest.py
index 1666237..a96bb8f 100644
--- a/lib/git_unittest.py
+++ b/lib/git_unittest.py
@@ -190,6 +190,12 @@
     ret = git.GetGitGitdir(self.fake_git_dir)
     self.assertEqual(ret, self.fake_git_dir)
 
+  def testGetGitGitdir_worktree(self):
+    dotgit = os.path.join(self.tempdir, '.git')
+    osutils.WriteFile(dotgit, 'gitdir: /foo')
+    ret = git.GetGitGitdir(self.tempdir)
+    self.assertEqual(ret, dotgit)
+
   def testGetGitGitdir_negative(self):
     ret = git.GetGitGitdir(self.tempdir)
     self.assertFalse(ret)