~bzr-pqm/bzr/bzr.dev

1 by mbp at sourcefrog
import from baz patch-364
1
# Bazaar-NG -- distributed version control
2
3
# Copyright (C) 2005 by Canonical Ltd
4
5
# This program is free software; you can redistribute it and/or modify
6
# it under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 2 of the License, or
8
# (at your option) any later version.
9
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.
14
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18
444 by Martin Pool
- cope on platforms with no urandom feature
19
import os, types, re, time, errno, sys
1185.3.28 by John Arbash Meinel
Adding knowledge about fifo/block/etc, they will be unknown/ignored.
20
from stat import (S_ISREG, S_ISDIR, S_ISLNK, ST_MODE, ST_SIZE,
21
        S_ISCHR, S_ISBLK, S_ISFIFO, S_ISSOCK)
1 by mbp at sourcefrog
import from baz patch-364
22
694 by Martin Pool
- weed out all remaining calls to bailout() and remove the function
23
from bzrlib.errors import BzrError
24
from bzrlib.trace import mutter
251 by mbp at sourcefrog
- factor out locale.getpreferredencoding()
25
import bzrlib
1 by mbp at sourcefrog
import from baz patch-364
26
27
def make_readonly(filename):
28
    """Make a filename read-only."""
29
    # TODO: probably needs to be fixed for windows
30
    mod = os.stat(filename).st_mode
31
    mod = mod & 0777555
32
    os.chmod(filename, mod)
33
34
35
def make_writable(filename):
36
    mod = os.stat(filename).st_mode
37
    mod = mod | 0200
38
    os.chmod(filename, mod)
39
40
1077 by Martin Pool
- avoid compiling REs at module load time
41
_QUOTE_RE = None
969 by Martin Pool
- Add less-sucky is_within_any
42
43
1 by mbp at sourcefrog
import from baz patch-364
44
def quotefn(f):
779 by Martin Pool
- better quotefn for windows: use doublequotes for strings with
45
    """Return a quoted filename filename
46
47
    This previously used backslash quoting, but that works poorly on
48
    Windows."""
49
    # TODO: I'm not really sure this is the best format either.x
1077 by Martin Pool
- avoid compiling REs at module load time
50
    global _QUOTE_RE
51
    if _QUOTE_RE == None:
52
        _QUOTE_RE = re.compile(r'([^a-zA-Z0-9.,:/_~-])')
53
        
779 by Martin Pool
- better quotefn for windows: use doublequotes for strings with
54
    if _QUOTE_RE.search(f):
55
        return '"' + f + '"'
56
    else:
57
        return f
1 by mbp at sourcefrog
import from baz patch-364
58
59
60
def file_kind(f):
61
    mode = os.lstat(f)[ST_MODE]
62
    if S_ISREG(mode):
63
        return 'file'
64
    elif S_ISDIR(mode):
65
        return 'directory'
20 by mbp at sourcefrog
don't abort on trees that happen to contain symlinks
66
    elif S_ISLNK(mode):
67
        return 'symlink'
1185.3.28 by John Arbash Meinel
Adding knowledge about fifo/block/etc, they will be unknown/ignored.
68
    elif S_ISCHR(mode):
69
        return 'chardev'
70
    elif S_ISBLK(mode):
71
        return 'block'
72
    elif S_ISFIFO(mode):
73
        return 'fifo'
74
    elif S_ISSOCK(mode):
75
        return 'socket'
1 by mbp at sourcefrog
import from baz patch-364
76
    else:
1185.3.28 by John Arbash Meinel
Adding knowledge about fifo/block/etc, they will be unknown/ignored.
77
        return 'unknown'
488 by Martin Pool
- new helper function kind_marker()
78
79
80
def kind_marker(kind):
81
    if kind == 'file':
82
        return ''
83
    elif kind == 'directory':
84
        return '/'
85
    elif kind == 'symlink':
86
        return '@'
87
    else:
88
        raise BzrError('invalid file kind %r' % kind)
1 by mbp at sourcefrog
import from baz patch-364
89
90
91
779 by Martin Pool
- better quotefn for windows: use doublequotes for strings with
92
def backup_file(fn):
93
    """Copy a file to a backup.
94
95
    Backups are named in GNU-style, with a ~ suffix.
96
97
    If the file is already a backup, it's not copied.
98
    """
99
    import os
100
    if fn[-1] == '~':
101
        return
102
    bfn = fn + '~'
103
104
    inf = file(fn, 'rb')
105
    try:
106
        content = inf.read()
107
    finally:
108
        inf.close()
109
    
110
    outf = file(bfn, 'wb')
111
    try:
112
        outf.write(content)
113
    finally:
114
        outf.close()
115
909 by Martin Pool
- merge John's code to give the tree root an explicit file id
116
def rename(path_from, path_to):
117
    """Basically the same as os.rename() just special for win32"""
118
    if sys.platform == 'win32':
119
        try:
120
            os.remove(path_to)
121
        except OSError, e:
122
            if e.errno != e.ENOENT:
123
                raise
124
    os.rename(path_from, path_to)
125
126
779 by Martin Pool
- better quotefn for windows: use doublequotes for strings with
127
128
129
1 by mbp at sourcefrog
import from baz patch-364
130
def isdir(f):
131
    """True if f is an accessible directory."""
132
    try:
133
        return S_ISDIR(os.lstat(f)[ST_MODE])
134
    except OSError:
135
        return False
136
137
138
139
def isfile(f):
140
    """True if f is a regular file."""
141
    try:
142
        return S_ISREG(os.lstat(f)[ST_MODE])
143
    except OSError:
144
        return False
145
146
485 by Martin Pool
- move commit code into its own module
147
def is_inside(dir, fname):
148
    """True if fname is inside dir.
969 by Martin Pool
- Add less-sucky is_within_any
149
    
150
    The parameters should typically be passed to os.path.normpath first, so
151
    that . and .. and repeated slashes are eliminated, and the separators
152
    are canonical for the platform.
153
    
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
154
    The empty string as a dir name is taken as top-of-tree and matches 
155
    everything.
156
    
969 by Martin Pool
- Add less-sucky is_within_any
157
    >>> is_inside('src', 'src/foo.c')
158
    True
159
    >>> is_inside('src', 'srccontrol')
160
    False
161
    >>> is_inside('src', 'src/a/a/a/foo.c')
162
    True
163
    >>> is_inside('foo.c', 'foo.c')
164
    True
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
165
    >>> is_inside('foo.c', '')
166
    False
167
    >>> is_inside('', 'foo.c')
168
    True
485 by Martin Pool
- move commit code into its own module
169
    """
969 by Martin Pool
- Add less-sucky is_within_any
170
    # XXX: Most callers of this can actually do something smarter by 
171
    # looking at the inventory
972 by Martin Pool
- less dodgy is_inside function
172
    if dir == fname:
173
        return True
174
    
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
175
    if dir == '':
176
        return True
177
    
972 by Martin Pool
- less dodgy is_inside function
178
    if dir[-1] != os.sep:
179
        dir += os.sep
180
    
181
    return fname.startswith(dir)
182
485 by Martin Pool
- move commit code into its own module
183
184
def is_inside_any(dir_list, fname):
185
    """True if fname is inside any of given dirs."""
186
    for dirname in dir_list:
187
        if is_inside(dirname, fname):
188
            return True
189
    else:
190
        return False
191
192
1 by mbp at sourcefrog
import from baz patch-364
193
def pumpfile(fromfile, tofile):
194
    """Copy contents of one file to another."""
195
    tofile.write(fromfile.read())
196
197
198
def uuid():
199
    """Return a new UUID"""
63 by mbp at sourcefrog
fix up uuid command
200
    try:
319 by Martin Pool
- remove trivial chomp() function
201
        return file('/proc/sys/kernel/random/uuid').readline().rstrip('\n')
63 by mbp at sourcefrog
fix up uuid command
202
    except IOError:
203
        return chomp(os.popen('uuidgen').readline())
204
1 by mbp at sourcefrog
import from baz patch-364
205
206
def sha_file(f):
207
    import sha
208
    if hasattr(f, 'tell'):
209
        assert f.tell() == 0
210
    s = sha.new()
320 by Martin Pool
- Compute SHA-1 of files in chunks
211
    BUFSIZE = 128<<10
212
    while True:
213
        b = f.read(BUFSIZE)
214
        if not b:
215
            break
216
        s.update(b)
1 by mbp at sourcefrog
import from baz patch-364
217
    return s.hexdigest()
218
219
220
def sha_string(f):
221
    import sha
222
    s = sha.new()
223
    s.update(f)
224
    return s.hexdigest()
225
226
227
124 by mbp at sourcefrog
- check file text for past revisions is correct
228
def fingerprint_file(f):
229
    import sha
230
    s = sha.new()
126 by mbp at sourcefrog
Use just one big read to fingerprint files
231
    b = f.read()
232
    s.update(b)
233
    size = len(b)
124 by mbp at sourcefrog
- check file text for past revisions is correct
234
    return {'size': size,
235
            'sha1': s.hexdigest()}
236
237
258 by Martin Pool
- Take email from ~/.bzr.conf/email
238
def config_dir():
239
    """Return per-user configuration directory.
240
241
    By default this is ~/.bzr.conf/
242
    
243
    TODO: Global option --config-dir to override this.
244
    """
245
    return os.path.expanduser("~/.bzr.conf")
246
247
252 by Martin Pool
- Don't use host fqdn for default user name, because DNS tends
248
def _auto_user_id():
246 by mbp at sourcefrog
- unicode decoding in getting email and userid strings
249
    """Calculate automatic user identification.
250
251
    Returns (realname, email).
252
252 by Martin Pool
- Don't use host fqdn for default user name, because DNS tends
253
    Only used when none is set in the environment or the id file.
246 by mbp at sourcefrog
- unicode decoding in getting email and userid strings
254
252 by Martin Pool
- Don't use host fqdn for default user name, because DNS tends
255
    This previously used the FQDN as the default domain, but that can
256
    be very slow on machines where DNS is broken.  So now we simply
257
    use the hostname.
1 by mbp at sourcefrog
import from baz patch-364
258
    """
251 by mbp at sourcefrog
- factor out locale.getpreferredencoding()
259
    import socket
246 by mbp at sourcefrog
- unicode decoding in getting email and userid strings
260
261
    # XXX: Any good way to get real user name on win32?
262
1 by mbp at sourcefrog
import from baz patch-364
263
    try:
264
        import pwd
265
        uid = os.getuid()
266
        w = pwd.getpwuid(uid)
251 by mbp at sourcefrog
- factor out locale.getpreferredencoding()
267
        gecos = w.pw_gecos.decode(bzrlib.user_encoding)
268
        username = w.pw_name.decode(bzrlib.user_encoding)
25 by Martin Pool
cope when gecos field doesn't have a comma
269
        comma = gecos.find(',')
270
        if comma == -1:
271
            realname = gecos
272
        else:
273
            realname = gecos[:comma]
256 by Martin Pool
- More handling of auto-username case
274
        if not realname:
275
            realname = username
246 by mbp at sourcefrog
- unicode decoding in getting email and userid strings
276
1 by mbp at sourcefrog
import from baz patch-364
277
    except ImportError:
246 by mbp at sourcefrog
- unicode decoding in getting email and userid strings
278
        import getpass
256 by Martin Pool
- More handling of auto-username case
279
        realname = username = getpass.getuser().decode(bzrlib.user_encoding)
252 by Martin Pool
- Don't use host fqdn for default user name, because DNS tends
280
256 by Martin Pool
- More handling of auto-username case
281
    return realname, (username + '@' + socket.gethostname())
252 by Martin Pool
- Don't use host fqdn for default user name, because DNS tends
282
283
1074 by Martin Pool
- check for email address in BRANCH_ROOT/.bzr/email, so you can
284
def _get_user_id(branch):
258 by Martin Pool
- Take email from ~/.bzr.conf/email
285
    """Return the full user id from a file or environment variable.
286
1074 by Martin Pool
- check for email address in BRANCH_ROOT/.bzr/email, so you can
287
    e.g. "John Hacker <jhacker@foo.org>"
288
289
    branch
290
        A branch to use for a per-branch configuration, or None.
291
292
    The following are searched in order:
293
294
    1. $BZREMAIL
295
    2. .bzr/email for this branch.
296
    3. ~/.bzr.conf/email
297
    4. $EMAIL
298
    """
252 by Martin Pool
- Don't use host fqdn for default user name, because DNS tends
299
    v = os.environ.get('BZREMAIL')
300
    if v:
301
        return v.decode(bzrlib.user_encoding)
1074 by Martin Pool
- check for email address in BRANCH_ROOT/.bzr/email, so you can
302
303
    if branch:
304
        try:
305
            return (branch.controlfile("email", "r") 
306
                    .read()
307
                    .decode(bzrlib.user_encoding)
308
                    .rstrip("\r\n"))
309
        except IOError, e:
310
            if e.errno != errno.ENOENT:
311
                raise
312
        except BzrError, e:
313
            pass
252 by Martin Pool
- Don't use host fqdn for default user name, because DNS tends
314
    
315
    try:
258 by Martin Pool
- Take email from ~/.bzr.conf/email
316
        return (open(os.path.join(config_dir(), "email"))
252 by Martin Pool
- Don't use host fqdn for default user name, because DNS tends
317
                .read()
318
                .decode(bzrlib.user_encoding)
319
                .rstrip("\r\n"))
256 by Martin Pool
- More handling of auto-username case
320
    except IOError, e:
321
        if e.errno != errno.ENOENT:
252 by Martin Pool
- Don't use host fqdn for default user name, because DNS tends
322
            raise e
323
324
    v = os.environ.get('EMAIL')
325
    if v:
326
        return v.decode(bzrlib.user_encoding)
327
    else:    
328
        return None
246 by mbp at sourcefrog
- unicode decoding in getting email and userid strings
329
330
1074 by Martin Pool
- check for email address in BRANCH_ROOT/.bzr/email, so you can
331
def username(branch):
246 by mbp at sourcefrog
- unicode decoding in getting email and userid strings
332
    """Return email-style username.
333
334
    Something similar to 'Martin Pool <mbp@sourcefrog.net>'
335
254 by Martin Pool
- Doc cleanups from Magnus Therning
336
    TODO: Check it's reasonably well-formed.
246 by mbp at sourcefrog
- unicode decoding in getting email and userid strings
337
    """
1074 by Martin Pool
- check for email address in BRANCH_ROOT/.bzr/email, so you can
338
    v = _get_user_id(branch)
252 by Martin Pool
- Don't use host fqdn for default user name, because DNS tends
339
    if v:
340
        return v
341
    
342
    name, email = _auto_user_id()
246 by mbp at sourcefrog
- unicode decoding in getting email and userid strings
343
    if name:
344
        return '%s <%s>' % (name, email)
345
    else:
346
        return email
1 by mbp at sourcefrog
import from baz patch-364
347
348
1074 by Martin Pool
- check for email address in BRANCH_ROOT/.bzr/email, so you can
349
def user_email(branch):
1 by mbp at sourcefrog
import from baz patch-364
350
    """Return just the email component of a username."""
1074 by Martin Pool
- check for email address in BRANCH_ROOT/.bzr/email, so you can
351
    e = _get_user_id(branch)
1 by mbp at sourcefrog
import from baz patch-364
352
    if e:
1077 by Martin Pool
- avoid compiling REs at module load time
353
        m = re.search(r'[\w+.-]+@[\w+.-]+', e)
1 by mbp at sourcefrog
import from baz patch-364
354
        if not m:
694 by Martin Pool
- weed out all remaining calls to bailout() and remove the function
355
            raise BzrError("%r doesn't seem to contain a reasonable email address" % e)
1 by mbp at sourcefrog
import from baz patch-364
356
        return m.group(0)
357
252 by Martin Pool
- Don't use host fqdn for default user name, because DNS tends
358
    return _auto_user_id()[1]
1 by mbp at sourcefrog
import from baz patch-364
359
    
360
361
362
def compare_files(a, b):
363
    """Returns true if equal in contents"""
74 by mbp at sourcefrog
compare_files: read in one page at a time rather than
364
    BUFSIZE = 4096
365
    while True:
366
        ai = a.read(BUFSIZE)
367
        bi = b.read(BUFSIZE)
368
        if ai != bi:
369
            return False
370
        if ai == '':
371
            return True
1 by mbp at sourcefrog
import from baz patch-364
372
373
374
49 by mbp at sourcefrog
fix local-time-offset calculation
375
def local_time_offset(t=None):
376
    """Return offset of local zone from GMT, either at present or at time t."""
73 by mbp at sourcefrog
fix time.localtime call for python 2.3
377
    # python2.3 localtime() can't take None
183 by mbp at sourcefrog
pychecker fixups
378
    if t == None:
73 by mbp at sourcefrog
fix time.localtime call for python 2.3
379
        t = time.time()
380
        
49 by mbp at sourcefrog
fix local-time-offset calculation
381
    if time.localtime(t).tm_isdst and time.daylight:
8 by mbp at sourcefrog
store committer's timezone in revision and show
382
        return -time.altzone
383
    else:
384
        return -time.timezone
385
386
    
387
def format_date(t, offset=0, timezone='original'):
1 by mbp at sourcefrog
import from baz patch-364
388
    ## TODO: Perhaps a global option to use either universal or local time?
389
    ## Or perhaps just let people set $TZ?
390
    assert isinstance(t, float)
391
    
8 by mbp at sourcefrog
store committer's timezone in revision and show
392
    if timezone == 'utc':
1 by mbp at sourcefrog
import from baz patch-364
393
        tt = time.gmtime(t)
394
        offset = 0
8 by mbp at sourcefrog
store committer's timezone in revision and show
395
    elif timezone == 'original':
23 by mbp at sourcefrog
format_date: handle revisions with no timezone offset
396
        if offset == None:
397
            offset = 0
16 by mbp at sourcefrog
fix inverted calculation for original timezone -> utc
398
        tt = time.gmtime(t + offset)
12 by mbp at sourcefrog
new --timezone option for bzr log
399
    elif timezone == 'local':
1 by mbp at sourcefrog
import from baz patch-364
400
        tt = time.localtime(t)
49 by mbp at sourcefrog
fix local-time-offset calculation
401
        offset = local_time_offset(t)
12 by mbp at sourcefrog
new --timezone option for bzr log
402
    else:
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
403
        raise BzrError("unsupported timezone format %r" % timezone,
404
                       ['options are "utc", "original", "local"'])
8 by mbp at sourcefrog
store committer's timezone in revision and show
405
1 by mbp at sourcefrog
import from baz patch-364
406
    return (time.strftime("%a %Y-%m-%d %H:%M:%S", tt)
8 by mbp at sourcefrog
store committer's timezone in revision and show
407
            + ' %+03d%02d' % (offset / 3600, (offset / 60) % 60))
1 by mbp at sourcefrog
import from baz patch-364
408
409
410
def compact_date(when):
411
    return time.strftime('%Y%m%d%H%M%S', time.gmtime(when))
412
    
413
414
415
def filesize(f):
416
    """Return size of given open file."""
417
    return os.fstat(f.fileno())[ST_SIZE]
418
1185.1.7 by Robert Collins
Nathaniel McCallums patch for urandom friendliness on aix.
419
# Define rand_bytes based on platform.
420
try:
421
    # Python 2.4 and later have os.urandom,
422
    # but it doesn't work on some arches
423
    os.urandom(1)
1 by mbp at sourcefrog
import from baz patch-364
424
    rand_bytes = os.urandom
1185.1.7 by Robert Collins
Nathaniel McCallums patch for urandom friendliness on aix.
425
except (NotImplementedError, AttributeError):
426
    # If python doesn't have os.urandom, or it doesn't work,
427
    # then try to first pull random data from /dev/urandom
428
    if os.path.exists("/dev/urandom"):
429
        rand_bytes = file('/dev/urandom', 'rb').read
430
    # Otherwise, use this hack as a last resort
431
    else:
432
        # not well seeded, but better than nothing
433
        def rand_bytes(n):
434
            import random
435
            s = ''
436
            while n:
437
                s += chr(random.randint(0, 255))
438
                n -= 1
439
            return s
1 by mbp at sourcefrog
import from baz patch-364
440
441
## TODO: We could later have path objects that remember their list
442
## decomposition (might be too tricksy though.)
443
444
def splitpath(p):
445
    """Turn string into list of parts.
446
447
    >>> splitpath('a')
448
    ['a']
449
    >>> splitpath('a/b')
450
    ['a', 'b']
451
    >>> splitpath('a/./b')
452
    ['a', 'b']
453
    >>> splitpath('a/.b')
454
    ['a', '.b']
455
    >>> splitpath('a/../b')
184 by mbp at sourcefrog
pychecker fixups
456
    Traceback (most recent call last):
1 by mbp at sourcefrog
import from baz patch-364
457
    ...
694 by Martin Pool
- weed out all remaining calls to bailout() and remove the function
458
    BzrError: sorry, '..' not allowed in path
1 by mbp at sourcefrog
import from baz patch-364
459
    """
460
    assert isinstance(p, types.StringTypes)
271 by Martin Pool
- Windows path fixes
461
462
    # split on either delimiter because people might use either on
463
    # Windows
464
    ps = re.split(r'[\\/]', p)
465
466
    rps = []
1 by mbp at sourcefrog
import from baz patch-364
467
    for f in ps:
468
        if f == '..':
694 by Martin Pool
- weed out all remaining calls to bailout() and remove the function
469
            raise BzrError("sorry, %r not allowed in path" % f)
271 by Martin Pool
- Windows path fixes
470
        elif (f == '.') or (f == ''):
471
            pass
472
        else:
473
            rps.append(f)
474
    return rps
1 by mbp at sourcefrog
import from baz patch-364
475
476
def joinpath(p):
477
    assert isinstance(p, list)
478
    for f in p:
183 by mbp at sourcefrog
pychecker fixups
479
        if (f == '..') or (f == None) or (f == ''):
694 by Martin Pool
- weed out all remaining calls to bailout() and remove the function
480
            raise BzrError("sorry, %r not allowed in path" % f)
271 by Martin Pool
- Windows path fixes
481
    return os.path.join(*p)
1 by mbp at sourcefrog
import from baz patch-364
482
483
484
def appendpath(p1, p2):
485
    if p1 == '':
486
        return p2
487
    else:
271 by Martin Pool
- Windows path fixes
488
        return os.path.join(p1, p2)
1 by mbp at sourcefrog
import from baz patch-364
489
    
490
491
def extern_command(cmd, ignore_errors = False):
492
    mutter('external command: %s' % `cmd`)
493
    if os.system(cmd):
494
        if not ignore_errors:
694 by Martin Pool
- weed out all remaining calls to bailout() and remove the function
495
            raise BzrError('command failed')
1 by mbp at sourcefrog
import from baz patch-364
496
763 by Martin Pool
- Patch from Torsten Marek to take commit messages through an
497
498
def _read_config_value(name):
499
    """Read a config value from the file ~/.bzr.conf/<name>
500
    Return None if the file does not exist"""
501
    try:
502
        f = file(os.path.join(config_dir(), name), "r")
503
        return f.read().decode(bzrlib.user_encoding).rstrip("\r\n")
504
    except IOError, e:
505
        if e.errno == errno.ENOENT:
506
            return None
507
        raise
508
509