~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/ignores.py

  • Committer: Martin Pool
  • Date: 2005-09-01 06:32:52 UTC
  • Revision ID: mbp@sourcefrog.net-20050901063252-00fc761bf1076759
- make target to build emacs TAGS file

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006 Canonical Ltd
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 2 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
 
 
17
 
"""Lists of ignore files, etc."""
18
 
 
19
 
import errno
20
 
 
21
 
import bzrlib
22
 
from bzrlib import (
23
 
    atomicfile,
24
 
    config,
25
 
    globbing,
26
 
    )
27
 
 
28
 
from trace import warning
29
 
 
30
 
# This was the full ignore list for bzr 0.8
31
 
# please keep these sorted (in C locale order) to aid merging
32
 
OLD_DEFAULTS = [
33
 
    '#*#',
34
 
    '*$',
35
 
    '*,v',
36
 
    '*.BAK',
37
 
    '*.a',
38
 
    '*.bak',
39
 
    '*.elc',
40
 
    '*.exe',
41
 
    '*.la',
42
 
    '*.lo',
43
 
    '*.o',
44
 
    '*.obj',
45
 
    '*.orig',
46
 
    '*.py[oc]',
47
 
    '*.so',
48
 
    '*.tmp',
49
 
    '*~',
50
 
    '.#*',
51
 
    '.*.sw[nop]',
52
 
    '.*.tmp',
53
 
    # Our setup tests dump .python-eggs in the bzr source tree root
54
 
    './.python-eggs',
55
 
    '.DS_Store',
56
 
    '.arch-ids',
57
 
    '.arch-inventory',
58
 
    '.bzr.log',
59
 
    '.del-*',
60
 
    '.git',
61
 
    '.hg',
62
 
    '.jamdeps'
63
 
    '.libs',
64
 
    '.make.state',
65
 
    '.sconsign*',
66
 
    '.svn',
67
 
    '.sw[nop]',    # vim editing nameless file
68
 
    '.tmp*',
69
 
    'BitKeeper',
70
 
    'CVS',
71
 
    'CVS.adm',
72
 
    'RCS',
73
 
    'SCCS',
74
 
    'TAGS',
75
 
    '_darcs',
76
 
    'aclocal.m4',
77
 
    'autom4te*',
78
 
    'config.h',
79
 
    'config.h.in',
80
 
    'config.log',
81
 
    'config.status',
82
 
    'config.sub',
83
 
    'stamp-h',
84
 
    'stamp-h.in',
85
 
    'stamp-h1',
86
 
    '{arch}',
87
 
]
88
 
 
89
 
 
90
 
# ~/.bazaar/ignore will be filled out using
91
 
# this ignore list, if it does not exist
92
 
# please keep these sorted (in C locale order) to aid merging
93
 
USER_DEFAULTS = [
94
 
    '*.a',
95
 
    '*.o',
96
 
    '*.py[co]',
97
 
    '*.so',
98
 
    '*.sw[nop]',
99
 
    '*~',
100
 
    '.#*',
101
 
    '[#]*#',
102
 
]
103
 
 
104
 
 
105
 
 
106
 
def parse_ignore_file(f):
107
 
    """Read in all of the lines in the file and turn it into an ignore list
108
 
    
109
 
    Continue in the case of utf8 decoding errors, and emit a warning when 
110
 
    such and error is found. Optimise for the common case -- no decoding 
111
 
    errors.
112
 
    """
113
 
    ignored = set()
114
 
    ignore_file = f.read()
115
 
    try:
116
 
        # Try and parse whole ignore file at once.
117
 
        unicode_lines = ignore_file.decode('utf8').split('\n')
118
 
    except UnicodeDecodeError:
119
 
        # Otherwise go though line by line and pick out the 'good'
120
 
        # decodable lines
121
 
        lines = ignore_file.split('\n')
122
 
        unicode_lines = []    
123
 
        for line_number, line in enumerate(lines):
124
 
            try:
125
 
                unicode_lines.append(line.decode('utf-8'))
126
 
            except UnicodeDecodeError:
127
 
                # report error about line (idx+1)
128
 
                warning('.bzrignore: On Line #%d, malformed utf8 character. '
129
 
                        'Ignoring line.' % (line_number+1))
130
 
    
131
 
    # Append each line to ignore list if it's not a comment line
132
 
    for line in unicode_lines:
133
 
        line = line.rstrip('\r\n')
134
 
        if not line or line.startswith('#'):
135
 
            continue
136
 
        ignored.add(globbing.normalize_pattern(line))
137
 
    return ignored
138
 
 
139
 
 
140
 
def get_user_ignores():
141
 
    """Get the list of user ignored files, possibly creating it."""
142
 
    path = config.user_ignore_config_filename()
143
 
    patterns = set(USER_DEFAULTS)
144
 
    try:
145
 
        f = open(path, 'rb')
146
 
    except (IOError, OSError), e:
147
 
        # open() shouldn't return an IOError without errno, but just in case
148
 
        err = getattr(e, 'errno', None)
149
 
        if err not in (errno.ENOENT,):
150
 
            raise
151
 
        # Create the ignore file, and just return the default
152
 
        # We want to ignore if we can't write to the file
153
 
        # since get_* should be a safe operation
154
 
        try:
155
 
            _set_user_ignores(USER_DEFAULTS)
156
 
        except (IOError, OSError), e:
157
 
            if e.errno not in (errno.EPERM,):
158
 
                raise
159
 
        return patterns
160
 
 
161
 
    try:
162
 
        return parse_ignore_file(f)
163
 
    finally:
164
 
        f.close()
165
 
 
166
 
 
167
 
def _set_user_ignores(patterns):
168
 
    """Fill out the user ignore file with the given patterns
169
 
 
170
 
    This may raise an error if it doesn't have permission to
171
 
    write to the user ignore file.
172
 
    This is mostly used for testing, since it would be
173
 
    bad form to rewrite a user's ignore list.
174
 
    bzrlib only writes this file if it does not exist.
175
 
    """
176
 
    ignore_path = config.user_ignore_config_filename()
177
 
    config.ensure_config_dir_exists()
178
 
 
179
 
    # Create an empty file
180
 
    f = open(ignore_path, 'wb')
181
 
    try:
182
 
        for pattern in patterns:
183
 
            f.write(pattern.encode('utf8') + '\n')
184
 
    finally:
185
 
        f.close()
186
 
 
187
 
 
188
 
def add_unique_user_ignores(new_ignores):
189
 
    """Add entries to the user's ignore list if not present.
190
 
 
191
 
    :param new_ignores: A list of ignore patterns
192
 
    :return: The list of ignores that were added
193
 
    """
194
 
    ignored = get_user_ignores()
195
 
    to_add = []
196
 
    for ignore in new_ignores:
197
 
        ignore = globbing.normalize_pattern(ignore)
198
 
        if ignore not in ignored:
199
 
            ignored.add(ignore)
200
 
            to_add.append(ignore)
201
 
 
202
 
    if not to_add:
203
 
        return []
204
 
 
205
 
    f = open(config.user_ignore_config_filename(), 'ab')
206
 
    try:
207
 
        for pattern in to_add:
208
 
            f.write(pattern.encode('utf8') + '\n')
209
 
    finally:
210
 
        f.close()
211
 
 
212
 
    return to_add
213
 
 
214
 
 
215
 
_runtime_ignores = set()
216
 
 
217
 
 
218
 
def add_runtime_ignores(ignores):
219
 
    """Add some ignore patterns that only exists in memory.
220
 
 
221
 
    This is used by some plugins that want bzr to ignore files,
222
 
    but don't want to change a users ignore list.
223
 
    (Such as a conversion script that needs to ignore temporary files,
224
 
    but does not want to modify the project's ignore list.)
225
 
 
226
 
    :param ignores: A list or generator of ignore patterns.
227
 
    :return: None
228
 
    """
229
 
    global _runtime_ignores
230
 
    _runtime_ignores.update(set(ignores))
231
 
 
232
 
 
233
 
def get_runtime_ignores():
234
 
    """Get the current set of runtime ignores."""
235
 
    return _runtime_ignores
236
 
 
237
 
 
238
 
def tree_ignores_add_patterns(tree, name_pattern_list):
239
 
    """Retrieve a list of ignores from the ignore file in a tree.
240
 
 
241
 
    :param tree: Tree to retrieve the ignore list from.
242
 
    :return:
243
 
    """
244
 
    ifn = tree.abspath(bzrlib.IGNORE_FILENAME)
245
 
    if tree.has_filename(ifn):
246
 
        f = open(ifn, 'rt')
247
 
        try:
248
 
            igns = f.read().decode('utf-8')
249
 
        finally:
250
 
            f.close()
251
 
    else:
252
 
        igns = ""
253
 
 
254
 
    # TODO: If the file already uses crlf-style termination, maybe
255
 
    # we should use that for the newly added lines?
256
 
 
257
 
    if igns and igns[-1] != '\n':
258
 
        igns += '\n'
259
 
    for name_pattern in name_pattern_list:
260
 
        igns += name_pattern + '\n'
261
 
 
262
 
    f = atomicfile.AtomicFile(ifn, 'wb')
263
 
    try:
264
 
        f.write(igns.encode('utf-8'))
265
 
        f.commit()
266
 
    finally:
267
 
        f.close()
268
 
 
269
 
    if not tree.path2id('.bzrignore'):
270
 
        tree.add(['.bzrignore'])