repository.py 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. # repository.py -- For dealing wih git repositories.
  2. # Copyright (C) 2007 James Westby <jw+debian@jameswestby.net>
  3. #
  4. # This program is free software; you can redistribute it and/or
  5. # modify it under the terms of the GNU General Public License
  6. # as published by the Free Software Foundation; version 2
  7. # of the License.
  8. #
  9. # This program is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. # GNU General Public License for more details.
  13. #
  14. # You should have received a copy of the GNU General Public License
  15. # along with this program; if not, write to the Free Software
  16. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  17. # MA 02110-1301, USA.
  18. import os
  19. from objects import (ShaFile,
  20. Commit,
  21. Tree,
  22. Blob,
  23. )
  24. objectdir = 'objects'
  25. symref = 'ref: '
  26. class Repository(object):
  27. ref_locs = ['', 'refs', 'refs/tags', 'refs/heads', 'refs/remotes']
  28. def __init__(self, root):
  29. self._basedir = root
  30. def basedir(self):
  31. return self._basedir
  32. def object_dir(self):
  33. return os.path.join(self.basedir(), objectdir)
  34. def _get_ref(self, file):
  35. f = open(file, 'rb')
  36. try:
  37. contents = f.read()
  38. if contents.startswith(symref):
  39. ref = contents[len(symref):]
  40. if ref[-1] == '\n':
  41. ref = ref[:-1]
  42. return self.ref(ref)
  43. assert len(contents) == 41, 'Invalid ref'
  44. return contents[:-1]
  45. finally:
  46. f.close()
  47. def ref(self, name):
  48. for dir in self.ref_locs:
  49. file = os.path.join(self.basedir(), dir, name)
  50. if os.path.exists(file):
  51. return self._get_ref(file)
  52. def head(self):
  53. return self.ref('HEAD')
  54. def _get_object(self, sha, cls):
  55. assert len(sha) == 40, "Incorrect length sha: %s" % str(sha)
  56. dir = sha[:2]
  57. file = sha[2:]
  58. path = os.path.join(self.object_dir(), dir, file)
  59. if not os.path.exists(path):
  60. return None
  61. return cls.from_file(path)
  62. def get_object(self, sha):
  63. return self._get_object(sha, ShaFile)
  64. def get_commit(self, sha):
  65. return self._get_object(sha, Commit)
  66. def get_tree(self, sha):
  67. return self._get_object(sha, Tree)
  68. def get_blob(self, sha):
  69. return self._get_object(sha, Blob)
  70. def revision_history(self, head):
  71. """Returns a list of the commits reachable from head.
  72. Returns a list of commit objects. the first of which will be the commit
  73. of head, then following theat will be the parents.
  74. Raises NotCommitError if any no commits are referenced, including if the
  75. head parameter isn't the sha of a commit.
  76. XXX: work out how to handle merges.
  77. """
  78. commit = self.get_commit(head)
  79. history = [commit]
  80. parents = commit.parents()
  81. parent_commits = [[]] * len(parents)
  82. i = 0
  83. for parent in parents:
  84. parent_commits[i] = self.revision_history(parent)
  85. i += 1
  86. for commit_list in parent_commits:
  87. for parent_commit in commit_list:
  88. if parent_commit in history:
  89. continue
  90. j = 0
  91. for main_commit in history:
  92. if main_commit.commit_time() < parent_commit.commit_time():
  93. break
  94. j += 1
  95. history.insert(j, parent_commit)
  96. return history