1 # repo.py -- For dealing wih git repositories.
2 # Copyright (C) 2007 James Westby <jw+debian@jameswestby.net>
3 # Copyright (C) 2008 Jelmer Vernooij <jelmer@samba.org>
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; version 2
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
22 from commit import Commit
23 from errors import MissingCommitError
24 from objects import (ShaFile,
29 from pack import load_packs
38 def __init__(self, name, ref):
45 ref_locs = ['', 'refs', 'refs/tags', 'refs/heads', 'refs/remotes']
47 def __init__(self, root):
48 controldir = os.path.join(root, ".git")
49 if os.path.exists(os.path.join(controldir, "objects")):
51 self._basedir = controldir
55 self.path = controldir
56 self.tags = [Tag(name, ref) for name, ref in self.get_tags().items()]
63 return os.path.join(self.basedir(), OBJECTDIR)
66 return os.path.join(self.object_dir(), PACKDIR)
69 if self._packs is None:
70 self._packs = list(load_packs(self.pack_dir()))
73 def _get_ref(self, file):
77 if contents.startswith(SYMREF):
78 ref = contents[len(SYMREF):]
82 assert len(contents) == 41, 'Invalid ref'
88 for dir in self.ref_locs:
89 file = os.path.join(self.basedir(), dir, name)
90 if os.path.exists(file):
91 return self._get_ref(file)
95 for root, dirs, files in os.walk(os.path.join(self.basedir(), 'refs', 'tags')):
97 ret[name] = self._get_ref(os.path.join(root, name))
102 for root, dirs, files in os.walk(os.path.join(self.basedir(), 'refs', 'heads')):
104 ret[name] = self._get_ref(os.path.join(root, name))
108 return self.ref('HEAD')
110 def _get_object(self, sha, cls):
111 assert len(sha) == 40, "Incorrect length sha: %s" % str(sha)
114 # Check from object dir
115 path = os.path.join(self.object_dir(), dir, file)
116 if os.path.exists(path):
117 return cls.from_file(path)
119 for pack in self._get_packs():
122 # Should this raise instead?
125 def get_object(self, sha):
126 return self._get_object(sha, ShaFile)
128 def commit(self, sha):
129 return self._get_object(sha, Commit)
131 def get_tree(self, sha):
132 return self._get_object(sha, Tree)
134 def get_blob(self, sha):
135 return self._get_object(sha, Blob)
137 def revision_history(self, head):
138 """Returns a list of the commits reachable from head.
140 Returns a list of commit objects. the first of which will be the commit
141 of head, then following theat will be the parents.
143 Raises NotCommitError if any no commits are referenced, including if the
144 head parameter isn't the sha of a commit.
146 XXX: work out how to handle merges.
148 # We build the list backwards, as parents are more likely to be older
150 pending_commits = [head]
152 while pending_commits != []:
153 head = pending_commits.pop(0)
154 commit = self.commit(head)
156 raise MissingCommitError(head)
157 if commit in history:
160 for known_commit in history:
161 if known_commit.commit_time > commit.commit_time:
164 history.insert(i, commit)
165 parents = commit.parents
166 pending_commits += parents
171 def init_bare(cls, path, mkdir=True):
172 for d in [["objects"],
181 os.mkdir(os.path.join(path, *d))
182 open(os.path.join(path, 'HEAD'), 'w').write("ref: refs/heads/master\n")
183 open(os.path.join(path, 'description'), 'w').write("Unnamed repository")
184 open(os.path.join(path, 'info', 'excludes'), 'w').write("")