~abentley/bzrtools/bzrtools.dev

« back to all changes in this revision

Viewing changes to bzrtools.py

  • Committer: Aaron Bentley
  • Date: 2005-09-20 15:02:04 UTC
  • Revision ID: abentley@panoramicfeedback.com-20050920150204-2abd154eca9213bc
Moved extention lists to dotgraph

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2009, 2011-2012 Aaron Bentley <aaron@aaronbentley.com>
2
 
# Copyright (C) 2007 John Arbash Meinel
 
1
# Copyright (C) 2005 Aaron Bentley
 
2
# <aaron.bentley@utoronto.ca>
3
3
#
4
4
#    This program is free software; you can redistribute it and/or modify
5
5
#    it under the terms of the GNU General Public License as published by
14
14
#    You should have received a copy of the GNU General Public License
15
15
#    along with this program; if not, write to the Free Software
16
16
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
17
import bzrlib
17
18
import os
18
 
import re
 
19
import os.path
 
20
import sys
19
21
import tempfile
20
22
import shutil
21
 
 
22
 
from bzrlib import urlutils
23
 
from bzrlib.errors import (
24
 
    BzrCommandError,
25
 
    NotBranchError,
26
 
    NoSuchFile,
27
 
    )
28
 
from bzrlib.bzrdir import BzrDir
29
 
from bzrlib.transport import get_transport
30
 
 
31
 
 
32
 
def short_committer(committer):
33
 
    new_committer = re.sub('<.*>', '', committer).strip(' ')
34
 
    if len(new_committer) < 2:
35
 
        return committer
36
 
    return new_committer
37
 
 
38
 
 
39
 
def apache_ls(t):
40
 
    """Screen-scrape Apache listings"""
41
 
    apache_dir = '<img border="0" src="/icons/folder.gif" alt="[dir]">'\
42
 
        ' <a href="'
43
 
    t = t.clone()
44
 
    t._remote_path = lambda x: t.base
45
 
    try:
46
 
        lines = t.get('')
47
 
    except NoSuchFile:
48
 
        return
49
 
    expr = re.compile('<a[^>]*href="([^>]*)\/"[^>]*>', flags=re.I)
50
 
    for line in lines:
51
 
        match = expr.search(line)
52
 
        if match is None:
53
 
            continue
54
 
        url = match.group(1)
55
 
        if url.startswith('http://') or url.startswith('/') or '../' in url:
56
 
            continue
57
 
        if '?' in url:
58
 
            continue
59
 
        yield url.rstrip('/')
60
 
 
61
 
 
62
 
def list_branches(t):
63
 
    def is_inside(branch):
64
 
        return bool(branch.base.startswith(t.base))
65
 
 
66
 
    if t.base.startswith('http://'):
67
 
        def evaluate(bzrdir):
68
 
            try:
69
 
                branch = bzrdir.open_branch()
70
 
                if is_inside(branch):
71
 
                    return True, branch
72
 
                else:
73
 
                    return True, None
74
 
            except NotBranchError:
75
 
                return True, None
76
 
        return [b for b in BzrDir.find_bzrdirs(t, list_current=apache_ls,
77
 
                evaluate=evaluate) if b is not None]
78
 
    elif not t.listable():
79
 
        raise BzrCommandError("Can't list this type of location.")
80
 
    return [b for b in BzrDir.find_branches(t) if is_inside(b)]
81
 
 
82
 
 
83
 
def evaluate_branch_tree(bzrdir):
84
 
    try:
85
 
        tree, branch = bzrdir._get_tree_branch()
86
 
    except NotBranchError:
87
 
        return True, None
88
 
    else:
89
 
        return True, (branch, tree)
90
 
 
91
 
 
92
 
def iter_branch_tree(t, lister=None):
93
 
    return (x for x in BzrDir.find_bzrdirs(t, evaluate=evaluate_branch_tree,
94
 
            list_current=lister) if x is not None)
95
 
 
96
 
 
97
 
def open_from_url(location):
98
 
    location = urlutils.normalize_url(location)
99
 
    dirname, basename = urlutils.split(location)
100
 
    if location.endswith('/') and not basename.endswith('/'):
101
 
        basename += '/'
102
 
    return get_transport(dirname).get(basename)
103
 
 
 
23
from subprocess import Popen, PIPE
 
24
 
 
25
def temp_branch():
 
26
    dirname = tempfile.mkdtemp("temp-branch")
 
27
    return bzrlib.branch.Branch.initialize(dirname)
 
28
 
 
29
def rm_branch(br):
 
30
    shutil.rmtree(br.base)
 
31
 
 
32
def is_clean(cur_branch):
 
33
    """
 
34
    Return true if no files are modifed or unknown
 
35
    >>> import bzrlib.add
 
36
    >>> br = temp_branch()
 
37
    >>> is_clean(br)
 
38
    (True, [])
 
39
    >>> fooname = os.path.join(br.base, "foo")
 
40
    >>> file(fooname, "wb").write("bar")
 
41
    >>> is_clean(br)
 
42
    (True, ['foo'])
 
43
    >>> bzrlib.add.smart_add_branch(br, [br.base])
 
44
    1
 
45
    >>> is_clean(br)
 
46
    (False, [])
 
47
    >>> br.commit("added file")
 
48
    added foo
 
49
    >>> is_clean(br)
 
50
    (True, [])
 
51
    >>> rm_branch(br)
 
52
    """
 
53
    from bzrlib.diff import compare_trees
 
54
    old_tree = cur_branch.basis_tree()
 
55
    new_tree = cur_branch.working_tree()
 
56
    non_source = []
 
57
    for path, file_class, kind, file_id in new_tree.list_files():
 
58
        if file_class in ('?', 'I'):
 
59
            non_source.append(path)
 
60
    delta = compare_trees(old_tree, new_tree, want_unchanged=False)
 
61
    if len(delta.added) > 0 or len(delta.removed) > 0 or \
 
62
        len(delta.modified) > 0:
 
63
        return False, non_source
 
64
    return True, non_source 
 
65
 
 
66
def set_pull_data(br, location, rev_id):
 
67
    pull_file = file (br.controlfilename("x-pull-data"), "wb")
 
68
    pull_file.write("%s\n%s\n" % (location, rev_id))
 
69
 
 
70
def get_pull_data(br):
 
71
    """
 
72
    >>> br = temp_branch()
 
73
    >>> get_pull_data(br)
 
74
    (None, None)
 
75
    >>> set_pull_data(br, 'http://somewhere', '888-777')
 
76
    >>> get_pull_data(br)
 
77
    ('http://somewhere', '888-777')
 
78
    >>> rm_branch(br)
 
79
    """
 
80
    filename = br.controlfilename("x-pull-data")
 
81
    if not os.path.exists(filename):
 
82
        return (None, None)
 
83
    pull_file = file (filename, "rb")
 
84
    location, rev_id = [f.rstrip('\n') for f in pull_file]
 
85
    return location, rev_id
 
86
 
 
87
def set_push_data(br, location):
 
88
    push_file = file (br.controlfilename("x-push-data"), "wb")
 
89
    push_file.write("%s\n" % location)
 
90
 
 
91
def get_push_data(br):
 
92
    """
 
93
    >>> br = temp_branch()
 
94
    >>> get_push_data(br) is None
 
95
    True
 
96
    >>> set_push_data(br, 'http://somewhere')
 
97
    >>> get_push_data(br)
 
98
    'http://somewhere'
 
99
    >>> rm_branch(br)
 
100
    """
 
101
    filename = br.controlfilename("x-push-data")
 
102
    if not os.path.exists(filename):
 
103
        return None
 
104
    push_file = file (filename, "rb")
 
105
    (location,) = [f.rstrip('\n') for f in push_file]
 
106
    return location
 
107
 
 
108
"""
 
109
>>> shell_escape('hello')
 
110
'\h\e\l\l\o'
 
111
"""
 
112
def shell_escape(arg):
 
113
    return "".join(['\\'+c for c in arg])
 
114
 
 
115
def safe_system(args):
 
116
    """
 
117
    >>> real_system = os.system
 
118
    >>> os.system = sys.stdout.write
 
119
    >>> safe_system(['a', 'b', 'cd'])
 
120
    \\a \\b \\c\\d
 
121
    >>> os.system = real_system
 
122
    """
 
123
    arg_str = " ".join([shell_escape(a) for a in args])
 
124
    return os.system(arg_str)
 
125
 
 
126
def rsync(source, target, ssh=False, excludes=()):
 
127
    """
 
128
    >>> real_system = os.system
 
129
    >>> os.system = sys.stdout.write
 
130
    >>> rsync("a", "b")
 
131
    ['rsync', '-av', '--delete', 'a', 'b']
 
132
    >>> rsync("a", "b", excludes=("*.py",))
 
133
    ['rsync', '-av', '--delete', '--exclude-from', '-', 'a', 'b']
 
134
    >>> os.system = real_system
 
135
    """
 
136
    cmd = ["rsync", "-av", "--delete"]
 
137
    if ssh:
 
138
        cmd.extend(('-e', 'ssh'))
 
139
    if len(excludes) > 0:
 
140
        cmd.extend(('--exclude-from', '-'))
 
141
    cmd.extend((source, target))
 
142
    proc = Popen(cmd, stdin=PIPE)
 
143
    proc.stdin.write('\n'.join(excludes)+'\n')
 
144
    proc.stdin.close()
 
145
    proc.wait()
 
146
    return cmd
 
147
 
 
148
exclusions = ('.bzr/x-push-data', '.bzr/x-pull-data', '.bzr/stat-cache')
 
149
 
 
150
 
 
151
def push(cur_branch, location=None):
 
152
    push_location = get_push_data(cur_branch)
 
153
    if location is not None:
 
154
        if not location.endswith('/'):
 
155
            location += '/'
 
156
        push_location = location
 
157
    
 
158
    if push_location is None:
 
159
        print "No push location saved.  Please specify one on the command line."
 
160
        sys.exit(1)
 
161
 
 
162
    clean, non_source = is_clean(cur_branch)
 
163
    if not clean:
 
164
        print """Error: This tree has uncommitted changes or unknown (?) files.
 
165
Use "bzr status" to list them."""
 
166
        sys.exit(1)
 
167
    non_source.extend(exclusions)
 
168
 
 
169
    print "Pushing to %s" % push_location
 
170
    rsync(cur_branch.base+'/', push_location, ssh=True, excludes=non_source)
 
171
 
 
172
    set_push_data(cur_branch, push_location)
104
173
 
105
174
def run_tests():
106
175
    import doctest