19
def commit(branch, message,
19
def commit(branch, message, timestamp=None, timezone=None,
26
22
"""Commit working copy as a new revision.
28
24
The basic approach is to add all the file texts into the
42
38
timestamp -- if not None, seconds-since-epoch for a
43
39
postdated/predated commit.
46
If true, commit only those files.
49
If set, use this as the new revision id.
50
Useful for test or import commands that need to tightly
51
control what revisions are assigned. If you duplicate
52
a revision id that exists elsewhere it is your own fault.
53
If null (default), a time/random revision id is generated.
56
42
import os, time, tempfile
58
44
from inventory import Inventory
59
45
from osutils import isdir, isfile, sha_string, quotefn, \
60
local_time_offset, username, kind_marker, is_inside_any
46
local_time_offset, username
62
48
from branch import gen_file_id
63
49
from errors import BzrError
64
50
from revision import Revision
51
from textui import show_status
65
52
from trace import mutter, note
67
54
branch._need_writelock()
56
## TODO: Show branch names
58
# TODO: Don't commit if there are no changes, unless forced?
69
60
# First walk over the working inventory; and both update that
70
61
# and also build a new revision inventory. The revision
71
62
# inventory needs to hold the text-id, sha1 and size of the
74
65
# detect missing/deleted files, and remove them from the
75
66
# working inventory.
77
work_tree = branch.working_tree()
78
work_inv = work_tree.inventory
68
work_inv = branch.read_working_inventory()
80
70
basis = branch.basis_tree()
81
71
basis_inv = basis.inventory
85
note('looking for changes...')
87
73
for path, entry in work_inv.iter_entries():
74
## TODO: Cope with files that have gone missing.
88
76
## TODO: Check that the file kind has not changed from the previous
89
77
## revision of this file (if any).
94
82
file_id = entry.file_id
95
83
mutter('commit prep file %s, id %r ' % (p, file_id))
97
if specific_files and not is_inside_any(specific_files, path):
98
if basis_inv.has_id(file_id):
99
# carry over with previous state
100
inv.add(basis_inv[file_id].copy())
102
# omit this from committed inventory
106
if not work_tree.has_id(file_id):
85
if not os.path.exists(p):
86
mutter(" file is missing, removing from inventory")
108
print('deleted %s%s' % (path, kind_marker(entry.kind)))
109
mutter(" file is missing, removing from inventory")
88
show_status('D', entry.kind, quotefn(path))
110
89
missing_ids.append(file_id)
92
# TODO: Handle files that have been deleted
94
# TODO: Maybe a special case for empty files? Seems a
95
# waste to store them many times.
115
99
if basis_inv.has_id(file_id):
121
105
if entry.kind == 'directory':
123
raise BzrError("%s is entered as directory but not a directory"
107
raise BzrError("%s is entered as directory but not a directory" % quotefn(p))
125
108
elif entry.kind == 'file':
126
109
if not isfile(p):
127
110
raise BzrError("%s is entered as file but is not a file" % quotefn(p))
129
new_sha1 = work_tree.get_file_sha1(file_id)
112
content = file(p, 'rb').read()
114
entry.text_sha1 = sha_string(content)
115
entry.text_size = len(content)
131
117
old_ie = basis_inv.has_id(file_id) and basis_inv[file_id]
133
and old_ie.text_sha1 == new_sha1):
119
and (old_ie.text_size == entry.text_size)
120
and (old_ie.text_sha1 == entry.text_sha1)):
134
121
## assert content == basis.get_file(file_id).read()
135
entry.text_id = old_ie.text_id
136
entry.text_sha1 = new_sha1
137
entry.text_size = old_ie.text_size
122
entry.text_id = basis_inv[file_id].text_id
138
123
mutter(' unchanged from previous text_id {%s}' %
141
content = file(p, 'rb').read()
143
# calculate the sha again, just in case the file contents
144
# changed since we updated the cache
145
entry.text_sha1 = sha_string(content)
146
entry.text_size = len(content)
148
127
entry.text_id = gen_file_id(entry.name)
149
128
branch.text_store.add(content, entry.text_id)
150
129
mutter(' stored with text_id {%s}' % entry.text_id)
153
print('added %s' % path)
154
133
elif (old_ie.name == entry.name
155
134
and old_ie.parent_id == entry.parent_id):
156
print('modified %s' % path)
158
print('renamed %s' % path)
139
show_status(state, entry.kind, quotefn(path))
161
141
for file_id in missing_ids:
162
# Any files that have been deleted are now removed from the
163
# working inventory. Files that were not selected for commit
164
# are left as they were in the working inventory and ommitted
165
# from the revision inventory.
167
142
# have to do this later so we don't mess up the iterator.
168
143
# since parents may be removed before their children we