Merge property changes from 0.4.
[jelmer/subvertpy.git] / config.py
1 # Copyright (C) 2007-2008 Jelmer Vernooij <jelmer@samba.org>
2
3 # This program is free software; you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation; either version 3 of the License, or
6 # (at your option) any later version.
7
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 # GNU General Public License for more details.
12
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software
15 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16 """Stores per-repository settings."""
17
18 from bzrlib import osutils, urlutils, trace
19 from bzrlib.config import IniBasedConfig, config_dir, ensure_config_dir_exists, GlobalConfig, LocationConfig, Config, STORE_BRANCH, STORE_GLOBAL, STORE_LOCATION
20
21 from bzrlib.plugins.svn.core import SubversionException
22
23 import os
24
25 from bzrlib.plugins.svn import properties
26
27 # Settings are stored by UUID. 
28 # Data stored includes default branching scheme and locations the repository 
29 # was seen at.
30
31 def subversion_config_filename():
32     """Return per-user configuration ini file filename."""
33     return osutils.pathjoin(config_dir(), 'subversion.conf')
34
35
36 class SvnRepositoryConfig(IniBasedConfig):
37     """Per-repository settings."""
38
39     def __init__(self, uuid):
40         name_generator = subversion_config_filename
41         super(SvnRepositoryConfig, self).__init__(name_generator)
42         self.uuid = uuid
43         if not self.uuid in self._get_parser():
44             self._get_parser()[self.uuid] = {}
45
46     def set_branching_scheme(self, scheme, mandatory=False):
47         """Change the branching scheme.
48
49         :param scheme: New branching scheme.
50         """
51         self.set_user_option('branching-scheme', str(scheme))
52         self.set_user_option('branching-scheme-mandatory', str(mandatory))
53
54     def _get_user_option(self, name, use_global=True):
55         try:
56             return self._get_parser()[self.uuid][name]
57         except KeyError:
58             if not use_global:
59                 return None
60             return GlobalConfig()._get_user_option(name)
61
62     def get_reuse_revisions(self):
63         ret = self._get_user_option("reuse-revisions")
64         if ret is None:
65             return "other-branches"
66         assert ret in ("none", "other-branches", "removed-branches")
67         return ret
68
69     def get_branching_scheme(self):
70         """Get the branching scheme.
71
72         :return: BranchingScheme instance.
73         """
74         from mapping3.scheme import BranchingScheme
75         schemename = self._get_user_option("branching-scheme", use_global=False)
76         if schemename is not None:
77             return BranchingScheme.find_scheme(schemename.encode('ascii'))
78         return None
79
80     def get_set_revprops(self):
81         """Check whether or not bzr-svn should attempt to store Bazaar
82         revision properties in Subversion revision properties during commit."""
83         try:
84             return self._get_parser().get_bool(self.uuid, "set-revprops")
85         except KeyError:
86             return None
87
88     def get_supports_change_revprop(self):
89         """Check whether or not the repository supports changing existing 
90         revision properties."""
91         try:
92             return self._get_parser().get_bool(self.uuid, "supports-change-revprop")
93         except KeyError:
94             return None
95
96     def get_use_cache(self):
97         try:
98             return self._get_parser().get_bool(self.uuid, "use-cache")
99         except KeyError:
100             return True
101
102     def get_log_strip_trailing_newline(self):
103         """Check whether or not trailing newlines should be stripped in the 
104         Subversion log message (where support by the bzr<->svn mapping used)."""
105         try:
106             return self._get_parser().get_bool(self.uuid, "log-strip-trailing-newline")
107         except KeyError:
108             return False
109
110     def branching_scheme_is_mandatory(self):
111         """Check whether or not the branching scheme for this repository 
112         is mandatory.
113         """
114         try:
115             return self._get_parser().get_bool(self.uuid, "branching-scheme-mandatory")
116         except KeyError:
117             return False
118
119     def get_override_svn_revprops(self):
120         """Check whether or not bzr-svn should attempt to override Subversion revision 
121         properties after committing."""
122         def get_list(parser, section):
123             try:
124                 if parser.get_bool(section, "override-svn-revprops"):
125                     return [properties.PROP_REVISION_DATE, properties.PROP_REVISION_AUTHOR]
126                 return []
127             except ValueError:
128                 val = parser.get_value(section, "override-svn-revprops")
129                 if not isinstance(val, list):
130                     return [val]
131                 return val
132             except KeyError:
133                 return None
134         ret = get_list(self._get_parser(), self.uuid)
135         if ret is not None:
136             return ret
137         global_config = GlobalConfig()
138         return get_list(global_config._get_parser(), global_config._get_section())
139
140     def get_append_revisions_only(self):
141         """Check whether it is possible to remove revisions from the mainline.
142         """
143         try:
144             return self._get_parser().get_bool(self.uuid, "append_revisions_only")
145         except KeyError:
146             return None
147
148     def get_locations(self):
149         """Find the locations this repository has been seen at.
150
151         :return: Set with URLs.
152         """
153         val = self._get_user_option("locations", use_global=False)
154         if val is None:
155             return set()
156         return set(val.split(";"))
157
158     def add_location(self, location):
159         """Add a location for this repository.
160
161         :param location: URL of location to add.
162         """
163         locations = self.get_locations()
164         locations.add(location.rstrip("/"))
165         self.set_user_option('locations', ";".join(list(locations)))
166
167     def set_user_option(self, name, value):
168         """Change a user option.
169
170         :param name: Name of the option.
171         :param value: Value of the option.
172         """
173         conf_dir = os.path.dirname(self._get_filename())
174         ensure_config_dir_exists(conf_dir)
175         self._get_parser()[self.uuid][name] = value
176         f = open(self._get_filename(), 'wb')
177         self._get_parser().write(f)
178         f.close()
179
180
181 class BranchConfig(Config):
182     def __init__(self, branch):
183         super(BranchConfig, self).__init__()
184         self._location_config = None
185         self._repository_config = None
186         self.branch = branch
187         self.option_sources = (self._get_location_config, 
188                                self._get_repository_config)
189
190     def _get_location_config(self):
191         if self._location_config is None:
192             self._location_config = LocationConfig(self.branch.base)
193         return self._location_config
194
195     def _get_repository_config(self):
196         if self._repository_config is None:
197             self._repository_config = SvnRepositoryConfig(self.branch.repository.uuid)
198         return self._repository_config
199
200     def get_set_revprops(self):
201         return self._get_repository_config().get_set_revprops()
202
203     def get_log_strip_trailing_newline(self):
204         return self._get_repository_config().get_log_strip_trailing_newline()
205
206     def get_override_svn_revprops(self):
207         return self._get_repository_config().get_override_svn_revprops()
208
209     def _get_user_option(self, option_name):
210         """See Config._get_user_option."""
211         for source in self.option_sources:
212             value = source()._get_user_option(option_name)
213             if value is not None:
214                 return value
215         return None
216
217     def get_append_revisions_only(self):
218         return self.get_user_option("append_revision_only")
219
220     def _get_user_id(self):
221         """Get the user id from the 'email' key in the current section."""
222         return self._get_user_option('email')
223
224     def get_option(self, key, section=None):
225         if section == "BUILDDEB" and key == "merge":
226             revnum = self.branch.get_revnum()
227             try:
228                 props = self.branch.repository.transport.get_dir(urlutils.join(self.branch.get_branch_path(revnum), "debian"), revnum)[2]
229                 if props.has_key("mergeWithUpstream"):
230                     return "True"
231                 else:
232                     return "False"
233             except SubversionException:
234                 return None
235         return None
236
237     def set_user_option(self, name, value, store=STORE_LOCATION,
238         warn_masked=False):
239         if store == STORE_GLOBAL:
240             self._get_global_config().set_user_option(name, value)
241         elif store == STORE_BRANCH:
242             raise NotImplementedError("Saving in branch config not supported for Subversion branches")
243         else:
244             self._get_location_config().set_user_option(name, value, store)
245         if not warn_masked:
246             return
247         if store in (STORE_GLOBAL, STORE_BRANCH):
248             mask_value = self._get_location_config().get_user_option(name)
249             if mask_value is not None:
250                 trace.warning('Value "%s" is masked by "%s" from'
251                               ' locations.conf', value, mask_value)
252             else:
253                 if store == STORE_GLOBAL:
254                     branch_config = self._get_branch_data_config()
255                     mask_value = branch_config.get_user_option(name)
256                     if mask_value is not None:
257                         trace.warning('Value "%s" is masked by "%s" from'
258                                       ' branch.conf', value, mask_value)