60e9f80eb4900a61974565aaab500e732fdec2b5
[redakcja.git] / lib / hg.py
1 # -*- coding: utf-8 -*-
2 import os
3 import codecs
4 from mercurial import localrepo, ui, match, node, encoding, util
5 import mercurial.merge, mercurial.error
6
7 encoding.encoding = 'utf-8'
8
9
10 class RepositoryDoesNotExist(Exception):
11     pass
12
13 class Repository(object):
14     """Abstrakcja repozytorium Mercurial. DziaƂa z Mercurial w wersji 1.3.1."""
15     
16     def __init__(self, path, create=False):
17         self.ui = ui.ui()
18         self.ui.config('ui', 'quiet', 'true')
19         self.ui.config('ui', 'interactive', 'false')
20         
21         self.real_path = os.path.realpath(path)
22         self.repo = self.open_repository(self.real_path, create)
23         self._pending_files = []
24     
25     def open_repository(self, path, create=False):
26         if os.path.isdir(path):
27             try:
28                 return localrepo.localrepository(self.ui, path)
29             except mercurial.error.RepoError:
30                 # dir is not an hg repo, we must init it
31                 if create:
32                     return localrepo.localrepository(self.ui, path, create=1)
33         elif create:
34             os.makedirs(path)
35             return localrepo.localrepository(self.ui, path, create=1)
36         raise RepositoryDoesNotExist("Repository %s does not exist." % path)
37         
38     def all_files(self, branch='default'):
39         return self.in_branch(lambda: self._all_files(), branch)
40
41     def _all_files(self):
42         return list(self.repo[None])
43     
44     def get_file(self, path, branch='default'):
45         return self.in_branch(lambda: self._get_file(path), branch)
46
47     def _get_file(self, path):
48         return self.repo.wread(path)
49     
50     def add_file(self, path, value, branch='default'):
51         return self.in_branch(lambda: self._add_file(path, value), branch)
52
53     def _add_file(self, path, value):
54         return self.repo.wwrite(path, value.encode('utf-8'), [])
55 #        f = codecs.open(os.path.join(self.real_path, path), 'w', encoding='utf-8')
56 #        f.write(value)
57 #        f.close()
58 #        if path not in self._pending_files:
59 #            self._pending_files.append(path)
60
61     def _commit(self, message, user=None, key=None):
62         return self.repo.commit(text=message, user=user)
63     
64     def _commit2(self, message, key=None, user=None):
65         """
66         Commit unsynchronized data to disk.
67         Arguments::
68
69          - message: mercurial's changeset message
70          - key: supply to sync only one key
71         """
72         if isinstance(message, unicode):
73             message = message.encode('utf-8')
74         if isinstance(user, unicode):
75             user = user.encode('utf-8')
76         
77         commited = False
78         rev = None
79         files_to_add = []
80         files_to_remove = []
81         files_to_commit = []
82
83         # first of all, add absent data and clean removed
84         if key is None:
85             # will commit all keys
86             pending_files = self._pending_files
87         else:
88             if keys not in self._pending_files:
89                 # key isn't changed
90                 return None
91             else:
92                 pending_files = [key]
93         for path in pending_files:
94             files_to_commit.append(path)
95             if path in self.all_files():
96                 if not os.path.exists(os.path.join(self.real_path, path)):
97                     # file removed
98                     files_to_remove.append(path)
99             else:
100                 # file added
101                 files_to_add.append(path)
102         # hg add
103         if files_to_add:
104             self.repo.add(files_to_add)
105         # hg forget
106         if files_to_remove:
107             self.repo.forget(files_to_remove)
108         # ---- hg commit
109         if files_to_commit:
110             for i, f in enumerate(files_to_commit):
111                 if isinstance(f, unicode):
112                     files_to_commit[i] = f.encode('utf-8')
113             matcher = match.match(self.repo.root, self.repo.root, files_to_commit, default='path')
114             rev = self.repo.commit(message, user=user, match=matcher)
115             commited = True
116         # clean pending keys
117         for key in pending_files:
118             self._pending_files.remove(key)
119         # if commited:
120             # reread keys
121             # self._keys = self.get_persisted_objects_keys()
122             # return node.hex(rev)
123
124     def commit(self, message, key=None, user=None, branch='default'):
125         return self.in_branch(lambda: self._commit(message, key=key, user=user), branch)
126
127     def in_branch(self, action, bname='default'):
128         wlock = self.repo.wlock()
129         try:
130             old = self._switch_to_branch(bname)
131             try:
132                 # do some stuff
133                 return action()
134             finally:
135                 self._switch_to_branch(old)
136         finally:
137             wlock.release()
138
139     def _switch_to_branch(self, bname):
140         wlock = self.repo.wlock()
141         try:
142             current = self.repo[None].branch()
143             if current == bname:
144                 return current
145
146             tip = self.repo.branchtags()[bname]
147             upstats = mercurial.merge.update(self.repo, tip, False, True, None)
148             return current
149         except KeyError, ke:
150             raise RepositoryException("Can't switch to branch '%s': no such branch." % bname , ke)
151         except util.Abort, ae:
152             raise repositoryException("Can't switch to branch '%s': %s"  % (bname, ae.message), ae)
153         finally:
154             wlock.release()
155
156     def write_lock(self):
157         """Returns w write lock to the repository."""
158         return self.repo.wlock()
159
160
161 class RepositoryException(Exception):
162
163     def __init__(self, msg, cause=None):
164         Exception.__init__(self, msg)
165         self.cause = cause