/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
485 by Martin Pool
- move commit code into its own module
1
# Copyright (C) 2005 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
17
18
19
def commit(branch, message, timestamp=None, timezone=None,
20
           committer=None,
491 by Martin Pool
- Selective commit!
21
           verbose=True,
494 by Martin Pool
- commit takes an optional caller-specified revision id
22
           specific_files=None,
23
           rev_id=None):
485 by Martin Pool
- move commit code into its own module
24
    """Commit working copy as a new revision.
25
26
    The basic approach is to add all the file texts into the
27
    store, then the inventory, then make a new revision pointing
28
    to that inventory and store that.
29
30
    This is not quite safe if the working copy changes during the
31
    commit; for the moment that is simply not allowed.  A better
32
    approach is to make a temporary copy of the files before
33
    computing their hashes, and then add those hashes in turn to
34
    the inventory.  This should mean at least that there are no
35
    broken hash pointers.  There is no way we can get a snapshot
36
    of the whole directory at an instant.  This would also have to
37
    be robust against files disappearing, moving, etc.  So the
38
    whole thing is a bit hard.
39
40
    timestamp -- if not None, seconds-since-epoch for a
41
         postdated/predated commit.
491 by Martin Pool
- Selective commit!
42
43
    specific_files
44
        If true, commit only those files.
504 by Martin Pool
doc
45
46
    rev_id
47
        If set, use this as the new revision id.
48
        Useful for test or import commands that need to tightly
49
        control what revisions are assigned.  If you duplicate
50
        a revision id that exists elsewhere it is your own fault.
51
        If null (default), a time/random revision id is generated.
485 by Martin Pool
- move commit code into its own module
52
    """
53
54
    import os, time, tempfile
55
56
    from inventory import Inventory
57
    from osutils import isdir, isfile, sha_string, quotefn, \
491 by Martin Pool
- Selective commit!
58
         local_time_offset, username, kind_marker, is_inside_any
485 by Martin Pool
- move commit code into its own module
59
    
60
    from branch import gen_file_id
61
    from errors import BzrError
62
    from revision import Revision
63
    from trace import mutter, note
64
65
    branch._need_writelock()
66
67
    # First walk over the working inventory; and both update that
68
    # and also build a new revision inventory.  The revision
69
    # inventory needs to hold the text-id, sha1 and size of the
70
    # actual file versions committed in the revision.  (These are
71
    # not present in the working inventory.)  We also need to
72
    # detect missing/deleted files, and remove them from the
73
    # working inventory.
74
491 by Martin Pool
- Selective commit!
75
    work_tree = branch.working_tree()
76
    work_inv = work_tree.inventory
485 by Martin Pool
- move commit code into its own module
77
    inv = Inventory()
78
    basis = branch.basis_tree()
79
    basis_inv = basis.inventory
80
    missing_ids = []
491 by Martin Pool
- Selective commit!
81
501 by Martin Pool
- fix verboseness in commit command
82
    if verbose:
83
        note('looking for changes...')
84
        
485 by Martin Pool
- move commit code into its own module
85
    for path, entry in work_inv.iter_entries():
86
        ## TODO: Check that the file kind has not changed from the previous
87
        ## revision of this file (if any).
88
89
        entry = entry.copy()
90
91
        p = branch.abspath(path)
92
        file_id = entry.file_id
93
        mutter('commit prep file %s, id %r ' % (p, file_id))
94
491 by Martin Pool
- Selective commit!
95
        if specific_files and not is_inside_any(specific_files, path):
96
            if basis_inv.has_id(file_id):
97
                # carry over with previous state
98
                inv.add(basis_inv[file_id].copy())
99
            else:
100
                # omit this from committed inventory
101
                pass
102
            continue
103
104
        if not work_tree.has_id(file_id):
501 by Martin Pool
- fix verboseness in commit command
105
            if verbose:
106
                print('deleted %s%s' % (path, kind_marker(entry.kind)))
485 by Martin Pool
- move commit code into its own module
107
            mutter("    file is missing, removing from inventory")
108
            missing_ids.append(file_id)
109
            continue
110
111
        inv.add(entry)
112
113
        if basis_inv.has_id(file_id):
114
            old_kind = basis_inv[file_id].kind
115
            if old_kind != entry.kind:
116
                raise BzrError("entry %r changed kind from %r to %r"
117
                        % (file_id, old_kind, entry.kind))
118
119
        if entry.kind == 'directory':
120
            if not isdir(p):
491 by Martin Pool
- Selective commit!
121
                raise BzrError("%s is entered as directory but not a directory"
122
                               % quotefn(p))
485 by Martin Pool
- move commit code into its own module
123
        elif entry.kind == 'file':
124
            if not isfile(p):
125
                raise BzrError("%s is entered as file but is not a file" % quotefn(p))
126
491 by Martin Pool
- Selective commit!
127
            new_sha1 = work_tree.get_file_sha1(file_id)
485 by Martin Pool
- move commit code into its own module
128
129
            old_ie = basis_inv.has_id(file_id) and basis_inv[file_id]
130
            if (old_ie
491 by Martin Pool
- Selective commit!
131
                and old_ie.text_sha1 == new_sha1):
485 by Martin Pool
- move commit code into its own module
132
                ## assert content == basis.get_file(file_id).read()
491 by Martin Pool
- Selective commit!
133
                entry.text_id = old_ie.text_id
134
                entry.text_sha1 = new_sha1
135
                entry.text_size = old_ie.text_size
485 by Martin Pool
- move commit code into its own module
136
                mutter('    unchanged from previous text_id {%s}' %
137
                       entry.text_id)
138
            else:
491 by Martin Pool
- Selective commit!
139
                content = file(p, 'rb').read()
140
141
                entry.text_sha1 = sha_string(content)
142
                entry.text_size = len(content)
143
485 by Martin Pool
- move commit code into its own module
144
                entry.text_id = gen_file_id(entry.name)
145
                branch.text_store.add(content, entry.text_id)
146
                mutter('    stored with text_id {%s}' % entry.text_id)
501 by Martin Pool
- fix verboseness in commit command
147
                if verbose:
148
                    if not old_ie:
503 by Martin Pool
- fix verboseness in commit command
149
                        print('added %s' % path)
501 by Martin Pool
- fix verboseness in commit command
150
                    elif (old_ie.name == entry.name
151
                          and old_ie.parent_id == entry.parent_id):
503 by Martin Pool
- fix verboseness in commit command
152
                        print('modified %s' % path)
501 by Martin Pool
- fix verboseness in commit command
153
                    else:
503 by Martin Pool
- fix verboseness in commit command
154
                        print('renamed %s' % path)
485 by Martin Pool
- move commit code into its own module
155
156
157
    for file_id in missing_ids:
491 by Martin Pool
- Selective commit!
158
        # Any files that have been deleted are now removed from the
159
        # working inventory.  Files that were not selected for commit
160
        # are left as they were in the working inventory and ommitted
161
        # from the revision inventory.
162
        
485 by Martin Pool
- move commit code into its own module
163
        # have to do this later so we don't mess up the iterator.
164
        # since parents may be removed before their children we
165
        # have to test.
166
167
        # FIXME: There's probably a better way to do this; perhaps
168
        # the workingtree should know how to filter itbranch.
169
        if work_inv.has_id(file_id):
170
            del work_inv[file_id]
171
172
494 by Martin Pool
- commit takes an optional caller-specified revision id
173
    if rev_id is None:
174
        rev_id = _gen_revision_id(time.time())
175
    inv_id = rev_id
485 by Martin Pool
- move commit code into its own module
176
177
    inv_tmp = tempfile.TemporaryFile()
178
    inv.write_xml(inv_tmp)
179
    inv_tmp.seek(0)
180
    branch.inventory_store.add(inv_tmp, inv_id)
181
    mutter('new inventory_id is {%s}' % inv_id)
182
183
    branch._write_inventory(work_inv)
184
185
    if timestamp == None:
186
        timestamp = time.time()
187
188
    if committer == None:
189
        committer = username()
190
191
    if timezone == None:
192
        timezone = local_time_offset()
193
194
    mutter("building commit log message")
195
    rev = Revision(timestamp=timestamp,
196
                   timezone=timezone,
197
                   committer=committer,
198
                   precursor = branch.last_patch(),
199
                   message = message,
200
                   inventory_id=inv_id,
201
                   revision_id=rev_id)
202
203
    rev_tmp = tempfile.TemporaryFile()
204
    rev.write_xml(rev_tmp)
205
    rev_tmp.seek(0)
206
    branch.revision_store.add(rev_tmp, rev_id)
207
    mutter("new revision_id is {%s}" % rev_id)
208
209
    ## XXX: Everything up to here can simply be orphaned if we abort
210
    ## the commit; it will leave junk files behind but that doesn't
211
    ## matter.
212
213
    ## TODO: Read back the just-generated changeset, and make sure it
214
    ## applies and recreates the right state.
215
216
    ## TODO: Also calculate and store the inventory SHA1
217
    mutter("committing patch r%d" % (branch.revno() + 1))
218
219
    branch.append_revision(rev_id)
220
501 by Martin Pool
- fix verboseness in commit command
221
    if verbose:
222
        note("commited r%d" % branch.revno())
485 by Martin Pool
- move commit code into its own module
223
224
225
226
def _gen_revision_id(when):
227
    """Return new revision-id."""
228
    from binascii import hexlify
229
    from osutils import rand_bytes, compact_date, user_email
230
231
    s = '%s-%s-' % (user_email(), compact_date(when))
232
    s += hexlify(rand_bytes(8))
233
    return s
234
235