792
798
:ivar object_store: Dictionary-like object for accessing
794
:ivar refs: Dictionary-like object with the refs in this repository
800
:ivar refs: Dictionary-like object with the refs in this
797
804
def __init__(self, object_store, refs):
805
"""Open a repository.
807
This shouldn't be called directly, but rather through one of the
808
base classes, such as MemoryRepo or Repo.
810
:param object_store: Object store to use
811
:param refs: Refs container to use
798
813
self.object_store = object_store
801
816
def _init_files(self, bare):
802
817
"""Initialize a default set of named files."""
818
from dulwich.config import ConfigFile
803
819
self._put_named_file('description', "Unnamed repository")
804
self._put_named_file('config', ('[core]\n'
805
'repositoryformatversion = 0\n'
807
'bare = ' + str(bare).lower() + '\n'
808
'logallrefupdates = true\n'))
822
cf.set("core", "repositoryformatversion", "0")
823
cf.set("core", "filemode", "true")
824
cf.set("core", "bare", str(bare).lower())
825
cf.set("core", "logallrefupdates", "true")
827
self._put_named_file('config', f.getvalue())
809
828
self._put_named_file(os.path.join('info', 'exclude'), '')
811
830
def get_named_file(self, path):
879
898
def get_graph_walker(self, heads=None):
899
"""Retrieve a graph walker.
901
A graph walker is used by a remote repository (or proxy)
902
to find out which objects are present in this repository.
904
:param heads: Repository heads to use (optional)
905
:return: A graph walker object
880
907
if heads is None:
881
908
heads = self.refs.as_dict('refs/heads').values()
882
909
return self.object_store.get_graph_walker(heads)
913
940
def get_object(self, sha):
941
"""Retrieve the object with the specified SHA.
943
:param sha: SHA to retrieve
944
:return: A ShaFile object
945
:raise KeyError: when the object can not be found
914
947
return self.object_store[sha]
916
949
def get_parents(self, sha):
950
"""Retrieve the parents of a specific commit.
952
:param sha: SHA of the commit for which to retrieve the parents
953
:return: List of parents
917
955
return self.commit(sha).parents
919
957
def get_config(self):
921
p = ConfigParser.RawConfigParser()
922
p.read(os.path.join(self._controldir, 'config'))
923
return dict((section, dict(p.items(section)))
924
for section in p.sections())
958
"""Retrieve the config object.
960
:return: `ConfigFile` object for the ``.git/config`` file.
962
from dulwich.config import ConfigFile
963
path = os.path.join(self._controldir, 'config')
965
return ConfigFile.from_path(path)
966
except (IOError, OSError), e:
967
if e.errno != errno.ENOENT:
973
def get_config_stack(self):
974
"""Return a config stack for this repository.
976
This stack accesses the configuration for both this repository
977
itself (.git/config) and the global configuration, which usually
978
lives in ~/.gitconfig.
980
:return: `Config` instance for this repository
982
from dulwich.config import StackedConfig
983
backends = [self.get_config()] + StackedConfig.default_backends()
984
return StackedConfig(backends, writable=backends[0])
926
986
def commit(self, sha):
927
987
"""Retrieve the commit with a particular SHA.
1028
1088
return [e.commit for e in self.get_walker(include=[head])]
1030
1090
def __getitem__(self, name):
1091
"""Retrieve a Git object by SHA1 or ref.
1093
:param name: A Git object SHA1 or a ref name
1094
:return: A `ShaFile` object, such as a Commit or Blob
1095
:raise KeyError: when the specified ref or object does not exist
1031
1097
if len(name) in (20, 40):
1033
1099
return self.object_store[name]
1038
1104
except RefFormatError:
1039
1105
raise KeyError(name)
1042
raise NotImplementedError(self.__iter__)
1044
1107
def __contains__(self, name):
1108
"""Check if a specific Git object or ref is present.
1110
:param name: Git object SHA1 or ref name
1045
1112
if len(name) in (20, 40):
1046
1113
return name in self.object_store or name in self.refs
1048
1115
return name in self.refs
1050
1117
def __setitem__(self, name, value):
1120
:param name: ref name
1121
:param value: Ref value - either a ShaFile object, or a hex sha
1051
1123
if name.startswith("refs/") or name == "HEAD":
1052
1124
if isinstance(value, ShaFile):
1053
1125
self.refs[name] = value.id
1059
1131
raise ValueError(name)
1061
1133
def __delitem__(self, name):
1062
if name.startswith("refs") or name == "HEAD":
1136
:param name: Name of the ref to remove
1138
if name.startswith("refs/") or name == "HEAD":
1063
1139
del self.refs[name]
1065
1141
raise ValueError(name)
1143
def _get_user_identity(self):
1144
config = self.get_config_stack()
1145
return "%s <%s>" % (
1146
config.get(("user", ), "name"),
1147
config.get(("user", ), "email"))
1067
1149
def do_commit(self, message=None, committer=None,
1068
1150
author=None, commit_timestamp=None,
1069
1151
commit_timezone=None, author_timestamp=None,
1098
1180
if merge_heads is None:
1099
1181
# FIXME: Read merge heads from .git/MERGE_HEADS
1100
1182
merge_heads = []
1101
# TODO: Allow username to be missing, and get it from .git/config
1102
1183
if committer is None:
1103
raise ValueError("committer not set")
1184
committer = self._get_user_identity()
1104
1185
c.committer = committer
1105
1186
if commit_timestamp is None:
1106
1187
commit_timestamp = time.time()
1144
1225
class Repo(BaseRepo):
1145
"""A git repository backed by local disk."""
1226
"""A git repository backed by local disk.
1228
To open an existing repository, call the contructor with
1229
the path of the repository.
1231
To create a new repository, use the Repo.init class method.
1147
1234
def __init__(self, root):
1148
1235
if os.path.isdir(os.path.join(root, ".git", OBJECTDIR)):
1220
1307
:param paths: List of paths, relative to the repository path
1222
from dulwich.index import cleanup_mode
1309
if isinstance(paths, basestring):
1311
from dulwich.index import index_entry_from_stat
1223
1312
index = self.open_index()
1224
1313
for path in paths:
1225
1314
full_path = os.path.join(self.path, path)
1228
1316
st = os.stat(full_path)
1229
1317
except OSError:
1232
1320
del index[path]
1233
1321
except KeyError:
1234
pass # Doesn't exist in the index either
1322
pass # already removed
1236
1325
f = open(full_path, 'rb')
1238
1327
blob.data = f.read()
1241
1330
self.object_store.add_object(blob)
1242
# XXX: Cleanup some of the other file properties as well?
1243
index[path] = (st.st_ctime, st.st_mtime, st.st_dev, st.st_ino,
1244
cleanup_mode(st.st_mode), st.st_uid, st.st_gid, st.st_size,
1331
index[path] = index_entry_from_stat(st, blob.id, 0)
1248
def clone(self, target_path, mkdir=True, bare=False, origin="origin"):
1334
def clone(self, target_path, mkdir=True, bare=False,
1249
1336
"""Clone this repository.
1251
1338
:param target_path: Target path
1287
1374
def init(cls, path, mkdir=False):
1375
"""Create a new repository.
1377
:param path: Path in which to create the repository
1378
:param mkdir: Whether to create the directory
1379
:return: `Repo` instance
1290
1383
controldir = os.path.join(path, ".git")
1296
1389
def init_bare(cls, path):
1390
"""Create a new bare repository.
1392
``path`` should already exist and be an emty directory.
1394
:param path: Path to create bare repository in
1395
:return: a `Repo` instance
1297
1397
return cls._init_maybe_bare(path, True)
1299
1399
create = init_bare
1342
1442
def init_bare(cls, objects, refs):
1443
"""Create a new bare repository in memory.
1445
:param objects: Objects for the new repository,
1447
:param refs: Refs as dictionary, mapping names
1344
1451
for obj in objects:
1345
1452
ret.object_store.add_object(obj)