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 | ||
| 517
by Martin Pool - cleanup | 19 | def commit(branch, message, | 
| 20 | timestamp=None, | |
| 21 | timezone=None, | |
| 485
by Martin Pool - move commit code into its own module | 22 | committer=None, | 
| 491
by Martin Pool - Selective commit! | 23 | verbose=True, | 
| 494
by Martin Pool - commit takes an optional caller-specified revision id | 24 | specific_files=None, | 
| 882
by Martin Pool - Optionally raise EmptyCommit if there are no changes. Test for this. | 25 | rev_id=None, | 
| 885
by Martin Pool - commit command refuses unless something is changed or --unchanged is given | 26 | allow_pointless=True): | 
| 485
by Martin Pool - move commit code into its own module | 27 | """Commit working copy as a new revision. | 
| 28 | ||
| 29 |     The basic approach is to add all the file texts into the
 | |
| 30 |     store, then the inventory, then make a new revision pointing
 | |
| 31 |     to that inventory and store that.
 | |
| 32 | ||
| 33 |     This is not quite safe if the working copy changes during the
 | |
| 34 |     commit; for the moment that is simply not allowed.  A better
 | |
| 35 |     approach is to make a temporary copy of the files before
 | |
| 36 |     computing their hashes, and then add those hashes in turn to
 | |
| 37 |     the inventory.  This should mean at least that there are no
 | |
| 38 |     broken hash pointers.  There is no way we can get a snapshot
 | |
| 39 |     of the whole directory at an instant.  This would also have to
 | |
| 40 |     be robust against files disappearing, moving, etc.  So the
 | |
| 41 |     whole thing is a bit hard.
 | |
| 42 | ||
| 885
by Martin Pool - commit command refuses unless something is changed or --unchanged is given | 43 |     This raises PointlessCommit if there are no changes, no new merges,
 | 
| 44 |     and allow_pointless  is false.
 | |
| 882
by Martin Pool - Optionally raise EmptyCommit if there are no changes. Test for this. | 45 | |
| 485
by Martin Pool - move commit code into its own module | 46 |     timestamp -- if not None, seconds-since-epoch for a
 | 
| 47 |          postdated/predated commit.
 | |
| 491
by Martin Pool - Selective commit! | 48 | |
| 49 |     specific_files
 | |
| 50 |         If true, commit only those files.
 | |
| 504
by Martin Pool doc | 51 | |
| 52 |     rev_id
 | |
| 53 |         If set, use this as the new revision id.
 | |
| 54 |         Useful for test or import commands that need to tightly
 | |
| 55 |         control what revisions are assigned.  If you duplicate
 | |
| 56 |         a revision id that exists elsewhere it is your own fault.
 | |
| 57 |         If null (default), a time/random revision id is generated.
 | |
| 485
by Martin Pool - move commit code into its own module | 58 |     """
 | 
| 59 | ||
| 632
by Martin Pool - refactor commit code | 60 | import time, tempfile | 
| 485
by Martin Pool - move commit code into its own module | 61 | |
| 697
by Martin Pool - write out parent list for new revisions | 62 | from bzrlib.osutils import local_time_offset, username | 
| 63 | from bzrlib.branch import gen_file_id | |
| 885
by Martin Pool - commit command refuses unless something is changed or --unchanged is given | 64 | from bzrlib.errors import BzrError, PointlessCommit | 
| 697
by Martin Pool - write out parent list for new revisions | 65 | from bzrlib.revision import Revision, RevisionReference | 
| 66 | from bzrlib.trace import mutter, note | |
| 1182
by Martin Pool - more disentangling of xml storage format from objects | 67 | from bzrlib.xml import serializer_v4 | 
| 485
by Martin Pool - move commit code into its own module | 68 | |
| 610
by Martin Pool - replace Branch.lock(mode) with separate lock_read and lock_write | 69 | branch.lock_write() | 
| 580
by Martin Pool - Use explicit lock methods on a branch, rather than doing it | 70 | |
| 71 | try: | |
| 72 |         # First walk over the working inventory; and both update that
 | |
| 73 |         # and also build a new revision inventory.  The revision
 | |
| 74 |         # inventory needs to hold the text-id, sha1 and size of the
 | |
| 75 |         # actual file versions committed in the revision.  (These are
 | |
| 76 |         # not present in the working inventory.)  We also need to
 | |
| 77 |         # detect missing/deleted files, and remove them from the
 | |
| 78 |         # working inventory.
 | |
| 79 | ||
| 80 | work_tree = branch.working_tree() | |
| 81 | work_inv = work_tree.inventory | |
| 82 | basis = branch.basis_tree() | |
| 83 | basis_inv = basis.inventory | |
| 84 | ||
| 85 | if verbose: | |
| 1097
by Martin Pool - send trace messages out through python logging module | 86 |             # note('looking for changes...')
 | 
| 87 |             # print 'looking for changes...'
 | |
| 88 |             # disabled; should be done at a higher level
 | |
| 89 |             pass
 | |
| 580
by Martin Pool - Use explicit lock methods on a branch, rather than doing it | 90 | |
| 815
by Martin Pool - track pending-merges | 91 | pending_merges = branch.pending_merges() | 
| 92 | ||
| 882
by Martin Pool - Optionally raise EmptyCommit if there are no changes. Test for this. | 93 | missing_ids, new_inv, any_changes = \ | 
| 94 | _gather_commit(branch, | |
| 95 | work_tree, | |
| 96 | work_inv, | |
| 97 | basis_inv, | |
| 98 | specific_files, | |
| 99 | verbose) | |
| 100 | ||
| 885
by Martin Pool - commit command refuses unless something is changed or --unchanged is given | 101 | if not (any_changes or allow_pointless or pending_merges): | 
| 102 | raise PointlessCommit() | |
| 580
by Martin Pool - Use explicit lock methods on a branch, rather than doing it | 103 | |
| 104 | for file_id in missing_ids: | |
| 105 |             # Any files that have been deleted are now removed from the
 | |
| 106 |             # working inventory.  Files that were not selected for commit
 | |
| 107 |             # are left as they were in the working inventory and ommitted
 | |
| 108 |             # from the revision inventory.
 | |
| 109 | ||
| 110 |             # have to do this later so we don't mess up the iterator.
 | |
| 111 |             # since parents may be removed before their children we
 | |
| 112 |             # have to test.
 | |
| 113 | ||
| 114 |             # FIXME: There's probably a better way to do this; perhaps
 | |
| 115 |             # the workingtree should know how to filter itbranch.
 | |
| 116 | if work_inv.has_id(file_id): | |
| 117 | del work_inv[file_id] | |
| 118 | ||
| 119 | if rev_id is None: | |
| 1074
by Martin Pool - check for email address in BRANCH_ROOT/.bzr/email, so you can | 120 | rev_id = _gen_revision_id(branch, time.time()) | 
| 580
by Martin Pool - Use explicit lock methods on a branch, rather than doing it | 121 | inv_id = rev_id | 
| 122 | ||
| 123 | inv_tmp = tempfile.TemporaryFile() | |
| 1180
by Martin Pool - start splitting code for xml (de)serialization away from objects | 124 | |
| 125 | serializer_v4.write_inventory(new_inv, inv_tmp) | |
| 580
by Martin Pool - Use explicit lock methods on a branch, rather than doing it | 126 | inv_tmp.seek(0) | 
| 127 | branch.inventory_store.add(inv_tmp, inv_id) | |
| 128 | mutter('new inventory_id is {%s}' % inv_id) | |
| 129 | ||
| 672
by Martin Pool - revision records include the hash of their inventory and | 130 |         # We could also just sha hash the inv_tmp file
 | 
| 131 |         # however, in the case that branch.inventory_store.add()
 | |
| 132 |         # ever actually does anything special
 | |
| 133 | inv_sha1 = branch.get_inventory_sha1(inv_id) | |
| 134 | ||
| 580
by Martin Pool - Use explicit lock methods on a branch, rather than doing it | 135 | branch._write_inventory(work_inv) | 
| 136 | ||
| 137 | if timestamp == None: | |
| 138 | timestamp = time.time() | |
| 139 | ||
| 140 | if committer == None: | |
| 1074
by Martin Pool - check for email address in BRANCH_ROOT/.bzr/email, so you can | 141 | committer = username(branch) | 
| 580
by Martin Pool - Use explicit lock methods on a branch, rather than doing it | 142 | |
| 143 | if timezone == None: | |
| 144 | timezone = local_time_offset() | |
| 145 | ||
| 146 | mutter("building commit log message") | |
| 147 | rev = Revision(timestamp=timestamp, | |
| 148 | timezone=timezone, | |
| 149 | committer=committer, | |
| 150 | message = message, | |
| 151 | inventory_id=inv_id, | |
| 672
by Martin Pool - revision records include the hash of their inventory and | 152 | inventory_sha1=inv_sha1, | 
| 580
by Martin Pool - Use explicit lock methods on a branch, rather than doing it | 153 | revision_id=rev_id) | 
| 717
by Martin Pool - correctly set parent list when committing first | 154 | |
| 816
by Martin Pool - don't write precursor field in new revision xml | 155 | rev.parents = [] | 
| 717
by Martin Pool - correctly set parent list when committing first | 156 | precursor_id = branch.last_patch() | 
| 157 | if precursor_id: | |
| 158 | precursor_sha1 = branch.get_revision_sha1(precursor_id) | |
| 816
by Martin Pool - don't write precursor field in new revision xml | 159 | rev.parents.append(RevisionReference(precursor_id, precursor_sha1)) | 
| 160 | for merge_rev in pending_merges: | |
| 161 | rev.parents.append(RevisionReference(merge_rev)) | |
| 580
by Martin Pool - Use explicit lock methods on a branch, rather than doing it | 162 | |
| 163 | rev_tmp = tempfile.TemporaryFile() | |
| 1182
by Martin Pool - more disentangling of xml storage format from objects | 164 | serializer_v4.write_revision(rev, rev_tmp) | 
| 580
by Martin Pool - Use explicit lock methods on a branch, rather than doing it | 165 | rev_tmp.seek(0) | 
| 166 | branch.revision_store.add(rev_tmp, rev_id) | |
| 167 | mutter("new revision_id is {%s}" % rev_id) | |
| 168 | ||
| 169 |         ## XXX: Everything up to here can simply be orphaned if we abort
 | |
| 170 |         ## the commit; it will leave junk files behind but that doesn't
 | |
| 171 |         ## matter.
 | |
| 172 | ||
| 173 |         ## TODO: Read back the just-generated changeset, and make sure it
 | |
| 174 |         ## applies and recreates the right state.
 | |
| 175 | ||
| 176 |         ## TODO: Also calculate and store the inventory SHA1
 | |
| 177 | mutter("committing patch r%d" % (branch.revno() + 1)) | |
| 178 | ||
| 179 | branch.append_revision(rev_id) | |
| 180 | ||
| 818
by Martin Pool - Clear pending-merge list when committing. | 181 | branch.set_pending_merges([]) | 
| 182 | ||
| 580
by Martin Pool - Use explicit lock methods on a branch, rather than doing it | 183 | if verbose: | 
| 1097
by Martin Pool - send trace messages out through python logging module | 184 |             # disabled; should go through logging
 | 
| 185 |             # note("commited r%d" % branch.revno())
 | |
| 186 |             # print ("commited r%d" % branch.revno())
 | |
| 187 |             pass
 | |
| 580
by Martin Pool - Use explicit lock methods on a branch, rather than doing it | 188 | finally: | 
| 189 | branch.unlock() | |
| 485
by Martin Pool - move commit code into its own module | 190 | |
| 191 | ||
| 192 | ||
| 1074
by Martin Pool - check for email address in BRANCH_ROOT/.bzr/email, so you can | 193 | def _gen_revision_id(branch, when): | 
| 485
by Martin Pool - move commit code into its own module | 194 | """Return new revision-id.""" | 
| 195 | from binascii import hexlify | |
| 1074
by Martin Pool - check for email address in BRANCH_ROOT/.bzr/email, so you can | 196 | from bzrlib.osutils import rand_bytes, compact_date, user_email | 
| 485
by Martin Pool - move commit code into its own module | 197 | |
| 1074
by Martin Pool - check for email address in BRANCH_ROOT/.bzr/email, so you can | 198 | s = '%s-%s-' % (user_email(branch), compact_date(when)) | 
| 485
by Martin Pool - move commit code into its own module | 199 | s += hexlify(rand_bytes(8)) | 
| 200 | return s | |
| 201 | ||
| 202 | ||
| 632
by Martin Pool - refactor commit code | 203 | def _gather_commit(branch, work_tree, work_inv, basis_inv, specific_files, | 
| 204 | verbose): | |
| 205 | """Build inventory preparatory to commit. | |
| 206 | ||
| 882
by Martin Pool - Optionally raise EmptyCommit if there are no changes. Test for this. | 207 |     Returns missing_ids, new_inv, any_changes.
 | 
| 208 | ||
| 632
by Martin Pool - refactor commit code | 209 |     This adds any changed files into the text store, and sets their
 | 
| 210 |     test-id, sha and size in the returned inventory appropriately.
 | |
| 211 | ||
| 212 |     missing_ids
 | |
| 213 |         Modified to hold a list of files that have been deleted from
 | |
| 214 |         the working directory; these should be removed from the
 | |
| 215 |         working inventory.
 | |
| 216 |     """
 | |
| 217 | from bzrlib.inventory import Inventory | |
| 958
by Martin Pool - tidy up imports to use full module names | 218 | from bzrlib.osutils import isdir, isfile, sha_string, quotefn, \ | 
| 632
by Martin Pool - refactor commit code | 219 | local_time_offset, username, kind_marker, is_inside_any | 
| 220 | ||
| 958
by Martin Pool - tidy up imports to use full module names | 221 | from bzrlib.branch import gen_file_id | 
| 222 | from bzrlib.errors import BzrError | |
| 223 | from bzrlib.revision import Revision | |
| 632
by Martin Pool - refactor commit code | 224 | from bzrlib.trace import mutter, note | 
| 225 | ||
| 882
by Martin Pool - Optionally raise EmptyCommit if there are no changes. Test for this. | 226 | any_changes = False | 
| 909
by Martin Pool - merge John's code to give the tree root an explicit file id | 227 | inv = Inventory(work_inv.root.file_id) | 
| 632
by Martin Pool - refactor commit code | 228 | missing_ids = [] | 
| 229 | ||
| 230 | for path, entry in work_inv.iter_entries(): | |
| 231 |         ## TODO: Check that the file kind has not changed from the previous
 | |
| 232 |         ## revision of this file (if any).
 | |
| 233 | ||
| 234 | p = branch.abspath(path) | |
| 235 | file_id = entry.file_id | |
| 236 | mutter('commit prep file %s, id %r ' % (p, file_id)) | |
| 237 | ||
| 238 | if specific_files and not is_inside_any(specific_files, path): | |
| 880
by Martin Pool trace | 239 | mutter(' skipping file excluded from commit') | 
| 632
by Martin Pool - refactor commit code | 240 | if basis_inv.has_id(file_id): | 
| 241 |                 # carry over with previous state
 | |
| 242 | inv.add(basis_inv[file_id].copy()) | |
| 243 | else: | |
| 244 |                 # omit this from committed inventory
 | |
| 245 |                 pass
 | |
| 246 |             continue
 | |
| 247 | ||
| 248 | if not work_tree.has_id(file_id): | |
| 249 | if verbose: | |
| 250 | print('deleted %s%s' % (path, kind_marker(entry.kind))) | |
| 1185.1.17
by Robert Collins import scotts non verbose commit fix to allow non pointless commits | 251 | any_changes = True | 
| 632
by Martin Pool - refactor commit code | 252 | mutter(" file is missing, removing from inventory") | 
| 253 | missing_ids.append(file_id) | |
| 254 |             continue
 | |
| 255 | ||
| 633
by Martin Pool - Show added/renamed/modified messages from commit for non-file | 256 |         # this is present in the new inventory; may be new, modified or
 | 
| 257 |         # unchanged.
 | |
| 258 | old_ie = basis_inv.has_id(file_id) and basis_inv[file_id] | |
| 259 | ||
| 260 | entry = entry.copy() | |
| 632
by Martin Pool - refactor commit code | 261 | inv.add(entry) | 
| 262 | ||
| 633
by Martin Pool - Show added/renamed/modified messages from commit for non-file | 263 | if old_ie: | 
| 264 | old_kind = old_ie.kind | |
| 632
by Martin Pool - refactor commit code | 265 | if old_kind != entry.kind: | 
| 266 | raise BzrError("entry %r changed kind from %r to %r" | |
| 267 | % (file_id, old_kind, entry.kind)) | |
| 268 | ||
| 269 | if entry.kind == 'directory': | |
| 270 | if not isdir(p): | |
| 271 | raise BzrError("%s is entered as directory but not a directory" | |
| 272 | % quotefn(p)) | |
| 273 | elif entry.kind == 'file': | |
| 274 | if not isfile(p): | |
| 275 | raise BzrError("%s is entered as file but is not a file" % quotefn(p)) | |
| 276 | ||
| 277 | new_sha1 = work_tree.get_file_sha1(file_id) | |
| 278 | ||
| 279 | if (old_ie | |
| 280 | and old_ie.text_sha1 == new_sha1): | |
| 281 |                 ## assert content == basis.get_file(file_id).read()
 | |
| 282 | entry.text_id = old_ie.text_id | |
| 283 | entry.text_sha1 = new_sha1 | |
| 284 | entry.text_size = old_ie.text_size | |
| 285 | mutter(' unchanged from previous text_id {%s}' % | |
| 286 | entry.text_id) | |
| 287 | else: | |
| 288 | content = file(p, 'rb').read() | |
| 289 | ||
| 290 |                 # calculate the sha again, just in case the file contents
 | |
| 291 |                 # changed since we updated the cache
 | |
| 292 | entry.text_sha1 = sha_string(content) | |
| 293 | entry.text_size = len(content) | |
| 294 | ||
| 295 | entry.text_id = gen_file_id(entry.name) | |
| 296 | branch.text_store.add(content, entry.text_id) | |
| 297 | mutter(' stored with text_id {%s}' % entry.text_id) | |
| 633
by Martin Pool - Show added/renamed/modified messages from commit for non-file | 298 | |
| 299 | if verbose: | |
| 300 | marked = path + kind_marker(entry.kind) | |
| 301 | if not old_ie: | |
| 302 | print 'added', marked | |
| 882
by Martin Pool - Optionally raise EmptyCommit if there are no changes. Test for this. | 303 | any_changes = True | 
| 633
by Martin Pool - Show added/renamed/modified messages from commit for non-file | 304 | elif old_ie == entry: | 
| 305 | pass # unchanged | |
| 306 | elif (old_ie.name == entry.name | |
| 307 | and old_ie.parent_id == entry.parent_id): | |
| 308 | print 'modified', marked | |
| 882
by Martin Pool - Optionally raise EmptyCommit if there are no changes. Test for this. | 309 | any_changes = True | 
| 633
by Martin Pool - Show added/renamed/modified messages from commit for non-file | 310 | else: | 
| 311 | print 'renamed', marked | |
| 882
by Martin Pool - Optionally raise EmptyCommit if there are no changes. Test for this. | 312 | any_changes = True | 
| 1185.1.17
by Robert Collins import scotts non verbose commit fix to allow non pointless commits | 313 | elif old_ie != entry: | 
| 314 | any_changes = True | |
| 315 | ||
| 882
by Martin Pool - Optionally raise EmptyCommit if there are no changes. Test for this. | 316 | return missing_ids, inv, any_changes | 
| 633
by Martin Pool - Show added/renamed/modified messages from commit for non-file | 317 | |
| 318 |