113
115
if stacked.repository.supports_rich_root():
114
116
keys['root-id'] = set(['A-id'])
115
117
self.assertEqual(keys, repo.fileids_altered_by_revision_ids(['A-id']))
120
class FileIdInvolvedBase(TestCaseWithRepository):
122
def touch(self, tree, filename):
123
# use the trees transport to not depend on the tree's location or type.
124
tree.bzrdir.root_transport.append_bytes(filename, "appended line\n")
126
def compare_tree_fileids(self, branch, old_rev, new_rev):
127
old_tree = self.branch.repository.revision_tree(old_rev)
128
new_tree = self.branch.repository.revision_tree(new_rev)
129
delta = new_tree.changes_from(old_tree)
131
l2 = [id for path, id, kind in delta.added] + \
132
[id for oldpath, newpath, id, kind, text_modified, \
133
meta_modified in delta.renamed] + \
134
[id for path, id, kind, text_modified, meta_modified in \
139
class TestFileIdInvolved(FileIdInvolvedBase):
141
scenarios = all_repository_vf_format_scenarios()
144
super(TestFileIdInvolved, self).setUp()
145
# create three branches, and merge it
147
# ,-->J------>K (branch2)
149
# A --->B --->C---->D-->G (main)
151
# '--->E---+---->F (branch1)
154
# B changes: 'a-file-id-2006-01-01-abcd'
155
# C changes: Nothing (perfect merge)
156
# D changes: 'b-file-id-2006-01-01-defg'
157
# E changes: 'file-d'
158
# F changes: 'file-d'
159
# G changes: 'b-file-id-2006-01-01-defg'
160
# J changes: 'b-file-id-2006-01-01-defg'
161
# K changes: 'c-funky<file-id>quiji%bo'
163
main_wt = self.make_branch_and_tree('main')
164
main_branch = main_wt.branch
165
self.build_tree(["main/a","main/b","main/c"])
167
main_wt.add(['a', 'b', 'c'], ['a-file-id-2006-01-01-abcd',
168
'b-file-id-2006-01-01-defg',
169
'c-funky<file-id>quiji%bo'])
171
main_wt.commit("Commit one", rev_id="rev-A")
172
except errors.IllegalPath:
173
# TODO: jam 20060701 Consider raising a different exception
174
# newer formats do support this, and nothin can done to
175
# correct this test - its not a bug.
176
if sys.platform == 'win32':
177
raise tests.TestSkipped('Old repository formats do not'
178
' support file ids with <> on win32')
179
# This is not a known error condition
182
#-------- end A -----------
184
bt1 = self.make_branch_and_tree('branch1')
185
bt1.pull(main_branch)
187
self.build_tree(["branch1/d"])
188
bt1.add(['d'], ['file-d'])
189
bt1.commit("branch1, Commit one", rev_id="rev-E")
191
#-------- end E -----------
193
self.touch(main_wt, "a")
194
main_wt.commit("Commit two", rev_id="rev-B")
196
#-------- end B -----------
198
bt2 = self.make_branch_and_tree('branch2')
199
bt2.pull(main_branch)
200
branch2_branch = bt2.branch
201
set_executability(bt2, 'b', True)
202
bt2.commit("branch2, Commit one", rev_id="rev-J")
204
#-------- end J -----------
206
main_wt.merge_from_branch(b1)
207
main_wt.commit("merge branch1, rev-11", rev_id="rev-C")
209
#-------- end C -----------
211
bt1.rename_one("d","e")
212
bt1.commit("branch1, commit two", rev_id="rev-F")
214
#-------- end F -----------
217
bt2.commit("branch2, commit two", rev_id="rev-K")
219
#-------- end K -----------
221
main_wt.merge_from_branch(b1)
222
self.touch(main_wt, "b")
223
# D gets some funky characters to make sure the unescaping works
224
main_wt.commit("merge branch1, rev-12", rev_id="rev-<D>")
228
main_wt.merge_from_branch(branch2_branch)
229
main_wt.commit("merge branch1, rev-22", rev_id="rev-G")
232
self.branch = main_branch
234
def test_fileids_altered_between_two_revs(self):
235
self.branch.lock_read()
236
self.addCleanup(self.branch.unlock)
237
self.branch.repository.fileids_altered_by_revision_ids(["rev-J","rev-K"])
239
{'b-file-id-2006-01-01-defg':set(['rev-J']),
240
'c-funky<file-id>quiji%bo':set(['rev-K'])
242
self.branch.repository.fileids_altered_by_revision_ids(["rev-J","rev-K"]))
245
{'b-file-id-2006-01-01-defg': set(['rev-<D>']),
246
'file-d': set(['rev-F']),
248
self.branch.repository.fileids_altered_by_revision_ids(['rev-<D>', 'rev-F']))
252
'b-file-id-2006-01-01-defg': set(['rev-<D>', 'rev-G', 'rev-J']),
253
'c-funky<file-id>quiji%bo': set(['rev-K']),
254
'file-d': set(['rev-F']),
256
self.branch.repository.fileids_altered_by_revision_ids(
257
['rev-<D>', 'rev-G', 'rev-F', 'rev-K', 'rev-J']))
260
{'a-file-id-2006-01-01-abcd': set(['rev-B']),
261
'b-file-id-2006-01-01-defg': set(['rev-<D>', 'rev-G', 'rev-J']),
262
'c-funky<file-id>quiji%bo': set(['rev-K']),
263
'file-d': set(['rev-F']),
265
self.branch.repository.fileids_altered_by_revision_ids(
266
['rev-G', 'rev-F', 'rev-C', 'rev-B', 'rev-<D>', 'rev-K', 'rev-J']))
268
def fileids_altered_by_revision_ids(self, revision_ids):
269
"""This is a wrapper to strip TREE_ROOT if it occurs"""
270
repo = self.branch.repository
271
root_id = self.branch.basis_tree().get_root_id()
272
result = repo.fileids_altered_by_revision_ids(revision_ids)
273
if root_id in result:
277
def test_fileids_altered_by_revision_ids(self):
278
self.branch.lock_read()
279
self.addCleanup(self.branch.unlock)
281
{'a-file-id-2006-01-01-abcd':set(['rev-A']),
282
'b-file-id-2006-01-01-defg': set(['rev-A']),
283
'c-funky<file-id>quiji%bo': set(['rev-A']),
285
self.fileids_altered_by_revision_ids(["rev-A"]))
287
{'a-file-id-2006-01-01-abcd':set(['rev-B'])
289
self.branch.repository.fileids_altered_by_revision_ids(["rev-B"]))
291
{'b-file-id-2006-01-01-defg':set(['rev-<D>'])
293
self.branch.repository.fileids_altered_by_revision_ids(["rev-<D>"]))
295
def test_fileids_involved_full_compare(self):
296
# this tests that the result of each fileid_involved calculation
297
# along a revision history selects only the fileids selected by
298
# comparing the trees - no less, and no more. This is correct
299
# because in our sample data we do not revert any file ids along
300
# the revision history.
301
self.branch.lock_read()
302
self.addCleanup(self.branch.unlock)
304
history = self.branch.revision_history( )
306
if len(history) < 2: return
308
graph = self.branch.repository.get_graph()
309
for start in range(0,len(history)-1):
310
start_id = history[start]
311
for end in range(start+1,len(history)):
312
end_id = history[end]
313
unique_revs = graph.find_unique_ancestors(end_id, [start_id])
314
l1 = self.branch.repository.fileids_altered_by_revision_ids(
317
l2 = self.compare_tree_fileids(self.branch, start_id, end_id)
318
self.assertEquals(l1, l2)
321
class TestFileIdInvolvedNonAscii(FileIdInvolvedBase):
323
scenarios = all_repository_vf_format_scenarios()
325
def test_utf8_file_ids_and_revision_ids(self):
326
main_wt = self.make_branch_and_tree('main')
327
main_branch = main_wt.branch
328
self.build_tree(["main/a"])
330
file_id = u'a-f\xedle-id'.encode('utf8')
331
main_wt.add(['a'], [file_id])
332
revision_id = u'r\xe9v-a'.encode('utf8')
334
main_wt.commit('a', rev_id=revision_id)
335
except errors.NonAsciiRevisionId:
336
raise tests.TestSkipped('non-ascii revision ids not supported by %s'
337
% self.repository_format)
339
repo = main_wt.branch.repository
341
self.addCleanup(repo.unlock)
342
file_ids = repo.fileids_altered_by_revision_ids([revision_id])
343
root_id = main_wt.basis_tree().get_root_id()
344
if root_id in file_ids:
345
self.assertEqual({file_id:set([revision_id]),
346
root_id:set([revision_id])
349
self.assertEqual({file_id:set([revision_id])}, file_ids)
352
class TestFileIdInvolvedSuperset(FileIdInvolvedBase):
354
scenarios = all_repository_vf_format_scenarios()
357
super(TestFileIdInvolvedSuperset, self).setUp()
360
main_wt = self.make_branch_and_tree('main')
361
main_branch = main_wt.branch
362
self.build_tree(["main/a","main/b","main/c"])
364
main_wt.add(['a', 'b', 'c'], ['a-file-id-2006-01-01-abcd',
365
'b-file-id-2006-01-01-defg',
366
'c-funky<file-id>quiji\'"%bo'])
368
main_wt.commit("Commit one", rev_id="rev-A")
369
except errors.IllegalPath:
370
# TODO: jam 20060701 Consider raising a different exception
371
# newer formats do support this, and nothin can done to
372
# correct this test - its not a bug.
373
if sys.platform == 'win32':
374
raise tests.TestSkipped('Old repository formats do not'
375
' support file ids with <> on win32')
376
# This is not a known error condition
379
branch2_wt = self.make_branch_and_tree('branch2')
380
branch2_wt.pull(main_branch)
381
branch2_bzrdir = branch2_wt.bzrdir
382
branch2_branch = branch2_bzrdir.open_branch()
383
set_executability(branch2_wt, 'b', True)
384
branch2_wt.commit("branch2, Commit one", rev_id="rev-J")
386
main_wt.merge_from_branch(branch2_branch)
387
set_executability(main_wt, 'b', False)
388
main_wt.commit("merge branch1, rev-22", rev_id="rev-G")
391
self.branch = main_branch
393
def test_fileid_involved_full_compare2(self):
394
# this tests that fileids_altered_by_revision_ids returns
395
# more information than compare_tree can, because it
396
# sees each change rather than the aggregate delta.
397
self.branch.lock_read()
398
self.addCleanup(self.branch.unlock)
399
history = self.branch.revision_history()
402
graph = self.branch.repository.get_graph()
403
unique_revs = graph.find_unique_ancestors(new_rev, [old_rev])
405
l1 = self.branch.repository.fileids_altered_by_revision_ids(
409
l2 = self.compare_tree_fileids(self.branch, old_rev, new_rev)
410
self.assertNotEqual(l2, l1)
411
self.assertSubset(l2, l1)
414
def set_executability(wt, path, executable=True):
415
"""Set the executable bit for the file at path in the working tree
417
os.chmod() doesn't work on windows. But TreeTransform can mark or
418
unmark a file as executable.
420
file_id = wt.path2id(path)
421
tt = transform.TreeTransform(wt)
423
tt.set_executability(executable, tt.trans_id_tree_file_id(file_id))