/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_fileid_involved.py

  • Committer: John Arbash Meinel
  • Date: 2006-01-19 21:25:01 UTC
  • mto: (1185.50.49 bzr-jam-integration)
  • mto: This revision was merged to the branch mainline in revision 1549.
  • Revision ID: john@arbash-meinel.com-20060119212501-cb28cf532a878c41
fileid_involved needs to unescape the file id and revision id

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2010 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
 
 
17
 
import sys
18
 
import time
19
 
 
20
 
from bzrlib import (
21
 
    errors,
22
 
    remote,
23
 
    revision as _mod_revision,
24
 
    tests,
25
 
    transform,
26
 
    )
27
 
from bzrlib.tests import per_repository
28
 
 
29
 
 
30
 
class FileIdInvolvedBase(per_repository.TestCaseWithRepository):
31
 
 
32
 
    def touch(self, tree, filename):
33
 
        # use the trees transport to not depend on the tree's location or type.
34
 
        tree.bzrdir.root_transport.append_bytes(filename, "appended line\n")
35
 
 
36
 
    def compare_tree_fileids(self, branch, old_rev, new_rev):
37
 
        old_tree = self.branch.repository.revision_tree(old_rev)
38
 
        new_tree = self.branch.repository.revision_tree(new_rev)
39
 
        delta = new_tree.changes_from(old_tree)
40
 
 
41
 
        l2 = [id for path, id, kind in delta.added] + \
42
 
             [id for oldpath, newpath, id, kind, text_modified, \
43
 
                meta_modified in delta.renamed] + \
44
 
             [id for path, id, kind, text_modified, meta_modified in \
45
 
                delta.modified]
46
 
        return set(l2)
47
 
 
48
 
 
49
 
class TestFileIdInvolved(FileIdInvolvedBase):
 
1
 
 
2
 
 
3
 
 
4
from bzrlib.tests import TestCaseInTempDir
 
5
import os
 
6
from bzrlib.commit import commit
 
7
from bzrlib.add import smart_add
 
8
from bzrlib.branch import Branch
 
9
from bzrlib.clone import copy_branch
 
10
from bzrlib.merge import merge
 
11
from bzrlib.workingtree import WorkingTree
 
12
from bzrlib.delta import compare_trees
 
13
 
 
14
class TestFileIdInvolved(TestCaseInTempDir):
 
15
 
 
16
    def touch(self,filename):
 
17
        f = file(filename,"a")
 
18
        f.write("appended line\n")
 
19
        f.close( )
 
20
 
 
21
 
 
22
    def merge( self, branch_from, force=False ):
 
23
        from bzrlib.merge_core import ApplyMerge3
 
24
 
 
25
        merge([branch_from,-1],[None,None], merge_type=ApplyMerge3,
 
26
            check_clean=(not force) )
50
27
 
51
28
    def setUp(self):
52
29
        super(TestFileIdInvolved, self).setUp()
53
30
        # create three branches, and merge it
54
31
        #
55
 
        #          ,-->J------>K                (branch2)
56
 
        #         /             \
57
 
        #  A --->B --->C---->D-->G              (main)
58
 
        #  \          /     /
59
 
        #   '--->E---+---->F                    (branch1)
60
 
 
61
 
        # A changes:
62
 
        # B changes: 'a-file-id-2006-01-01-abcd'
63
 
        # C changes:  Nothing (perfect merge)
64
 
        # D changes: 'b-file-id-2006-01-01-defg'
65
 
        # E changes: 'file-d'
66
 
        # F changes: 'file-d'
67
 
        # G changes: 'b-file-id-2006-01-01-defg'
68
 
        # J changes: 'b-file-id-2006-01-01-defg'
69
 
        # K changes: 'c-funky<file-id>quiji%bo'
70
 
 
71
 
        main_wt = self.make_branch_and_tree('main')
72
 
        main_branch = main_wt.branch
73
 
        self.build_tree(["main/a","main/b","main/c"])
74
 
 
75
 
        main_wt.add(['a', 'b', 'c'], ['a-file-id-2006-01-01-abcd',
 
32
        #           /-->J ------>K                (branch2)
 
33
        #          /              \
 
34
        #  A ---> B --->C ---->D->G               (main)
 
35
        #  \           /      /
 
36
        #   \---> E---/----> F                 (branch1)
 
37
 
 
38
        os.mkdir("main")
 
39
        os.chdir("main")
 
40
 
 
41
        main_branch = Branch.initialize('.')
 
42
        self.build_tree(["a","b","c"])
 
43
 
 
44
        b = Branch.open('.')
 
45
        wt = b.working_tree()
 
46
        wt.add(['a', 'b', 'c'], ['a-file-id-2006-01-01-abcd',
76
47
                                 'b-file-id-2006-01-01-defg',
77
 
                                 'c-funky<file-id>quiji%bo'])
78
 
        try:
79
 
            main_wt.commit("Commit one", rev_id="rev-A")
80
 
        except errors.IllegalPath:
81
 
            # TODO: jam 20060701 Consider raising a different exception
82
 
            #       newer formats do support this, and nothin can done to
83
 
            #       correct this test - its not a bug.
84
 
            if sys.platform == 'win32':
85
 
                raise tests.TestSkipped('Old repository formats do not'
86
 
                                        ' support file ids with <> on win32')
87
 
            # This is not a known error condition
88
 
            raise
89
 
 
 
48
                                 'c-funky<file-id> quiji%bo'])
 
49
        commit(b, "Commit one", rev_id="rev-A")
 
50
        del b, wt
90
51
        #-------- end A -----------
91
52
 
92
 
        bt1 = self.make_branch_and_tree('branch1')
93
 
        bt1.pull(main_branch)
94
 
        b1 = bt1.branch
95
 
        self.build_tree(["branch1/d"])
96
 
        bt1.add(['d'], ['file-d'])
97
 
        bt1.commit("branch1, Commit one", rev_id="rev-E")
 
53
        copy_branch(main_branch,"../branch1")
 
54
        os.chdir("../branch1")
 
55
 
 
56
        #branch1_branch = Branch.open(".")
 
57
        self.build_tree(["d"])
 
58
        smart_add(".")
 
59
        commit(Branch.open("."), "branch1, Commit one", rev_id="rev-E")
98
60
 
99
61
        #-------- end E -----------
100
62
 
101
 
        self.touch(main_wt, "a")
102
 
        main_wt.commit("Commit two", rev_id="rev-B")
 
63
        os.chdir("../main")
 
64
        self.touch("a")
 
65
        commit(Branch.open("."), "Commit two", rev_id="rev-B")
103
66
 
104
67
        #-------- end B -----------
105
68
 
106
 
        bt2 = self.make_branch_and_tree('branch2')
107
 
        bt2.pull(main_branch)
108
 
        branch2_branch = bt2.branch
109
 
        set_executability(bt2, 'b', True)
110
 
        bt2.commit("branch2, Commit one", rev_id="rev-J")
 
69
        copy_branch(Branch.open("."),"../branch2")
 
70
        os.chdir("../branch2")
 
71
 
 
72
        branch2_branch = Branch.open(".")
 
73
        os.chmod("b",0770)
 
74
        commit(Branch.open("."), "branch2, Commit one", rev_id="rev-J")
111
75
 
112
76
        #-------- end J -----------
113
77
 
114
 
        main_wt.merge_from_branch(b1)
115
 
        main_wt.commit("merge branch1, rev-11", rev_id="rev-C")
 
78
        os.chdir("../main")
 
79
 
 
80
        self.merge("../branch1")
 
81
        commit(Branch.open("."), "merge branch1, rev-11", rev_id="rev-C")
116
82
 
117
83
        #-------- end C -----------
118
84
 
119
 
        bt1.rename_one("d","e")
120
 
        bt1.commit("branch1, commit two", rev_id="rev-F")
 
85
        os.chdir("../branch1")
 
86
        tree = WorkingTree('.', Branch.open("."))
 
87
        tree.rename_one("d","e")
 
88
        commit(Branch.open("."), "branch1, commit two", rev_id="rev-F")
 
89
 
121
90
 
122
91
        #-------- end F -----------
123
92
 
124
 
        self.touch(bt2, "c")
125
 
        bt2.commit("branch2, commit two", rev_id="rev-K")
 
93
        os.chdir("../branch2")
 
94
 
 
95
        self.touch("c")
 
96
        commit(Branch.open("."), "branch2, commit two", rev_id="rev-K")
126
97
 
127
98
        #-------- end K -----------
128
99
 
129
 
        main_wt.merge_from_branch(b1)
130
 
        self.touch(main_wt, "b")
 
100
        os.chdir("../main")
 
101
 
 
102
        self.touch("b")
 
103
        self.merge("../branch1",force=True)
 
104
 
131
105
        # D gets some funky characters to make sure the unescaping works
132
 
        main_wt.commit("merge branch1, rev-12", rev_id="rev-<D>")
 
106
        commit(Branch.open("."), "merge branch1, rev-12", rev_id="rev-<D>")
133
107
 
134
108
        # end D
135
109
 
136
 
        main_wt.merge_from_branch(branch2_branch)
137
 
        main_wt.commit("merge branch1, rev-22",  rev_id="rev-G")
 
110
        self.merge("../branch2")
 
111
        commit(Branch.open("."), "merge branch1, rev-22",  rev_id="rev-G")
138
112
 
139
113
        # end G
140
 
        self.branch = main_branch
141
 
 
142
 
    def test_fileids_altered_between_two_revs(self):
143
 
        self.branch.lock_read()
144
 
        self.addCleanup(self.branch.unlock)
145
 
        self.branch.repository.fileids_altered_by_revision_ids(["rev-J","rev-K"])
146
 
        self.assertEqual(
147
 
            {'b-file-id-2006-01-01-defg':set(['rev-J']),
148
 
             'c-funky<file-id>quiji%bo':set(['rev-K'])
149
 
             },
150
 
            self.branch.repository.fileids_altered_by_revision_ids(["rev-J","rev-K"]))
151
 
 
152
 
        self.assertEqual(
153
 
            {'b-file-id-2006-01-01-defg': set(['rev-<D>']),
154
 
             'file-d': set(['rev-F']),
155
 
             },
156
 
            self.branch.repository.fileids_altered_by_revision_ids(['rev-<D>', 'rev-F']))
157
 
 
158
 
        self.assertEqual(
159
 
            {
160
 
             'b-file-id-2006-01-01-defg': set(['rev-<D>', 'rev-G', 'rev-J']),
161
 
             'c-funky<file-id>quiji%bo': set(['rev-K']),
162
 
             'file-d': set(['rev-F']),
163
 
             },
164
 
            self.branch.repository.fileids_altered_by_revision_ids(
165
 
                ['rev-<D>', 'rev-G', 'rev-F', 'rev-K', 'rev-J']))
166
 
 
167
 
        self.assertEqual(
168
 
            {'a-file-id-2006-01-01-abcd': set(['rev-B']),
169
 
             'b-file-id-2006-01-01-defg': set(['rev-<D>', 'rev-G', 'rev-J']),
170
 
             'c-funky<file-id>quiji%bo': set(['rev-K']),
171
 
             'file-d': set(['rev-F']),
172
 
             },
173
 
            self.branch.repository.fileids_altered_by_revision_ids(
174
 
                ['rev-G', 'rev-F', 'rev-C', 'rev-B', 'rev-<D>', 'rev-K', 'rev-J']))
175
 
 
176
 
    def fileids_altered_by_revision_ids(self, revision_ids):
177
 
        """This is a wrapper to strip TREE_ROOT if it occurs"""
178
 
        repo = self.branch.repository
179
 
        root_id = self.branch.basis_tree().get_root_id()
180
 
        result = repo.fileids_altered_by_revision_ids(revision_ids)
181
 
        if root_id in result:
182
 
            del result[root_id]
183
 
        return result
184
 
 
185
 
    def test_fileids_altered_by_revision_ids(self):
186
 
        self.branch.lock_read()
187
 
        self.addCleanup(self.branch.unlock)
188
 
        self.assertEqual(
189
 
            {'a-file-id-2006-01-01-abcd':set(['rev-A']),
190
 
             'b-file-id-2006-01-01-defg': set(['rev-A']),
191
 
             'c-funky<file-id>quiji%bo': set(['rev-A']),
192
 
             },
193
 
            self.fileids_altered_by_revision_ids(["rev-A"]))
194
 
        self.assertEqual(
195
 
            {'a-file-id-2006-01-01-abcd':set(['rev-B'])
196
 
             },
197
 
            self.branch.repository.fileids_altered_by_revision_ids(["rev-B"]))
198
 
        self.assertEqual(
199
 
            {'b-file-id-2006-01-01-defg':set(['rev-<D>'])
200
 
             },
201
 
            self.branch.repository.fileids_altered_by_revision_ids(["rev-<D>"]))
202
 
 
203
 
    def test_fileids_involved_full_compare(self):
204
 
        # this tests that the result of each fileid_involved calculation
205
 
        # along a revision history selects only the fileids selected by
206
 
        # comparing the trees - no less, and no more. This is correct
207
 
        # because in our sample data we do not revert any file ids along
208
 
        # the revision history.
209
 
        self.branch.lock_read()
210
 
        self.addCleanup(self.branch.unlock)
 
114
        os.chdir("../main")
 
115
        self.branch = Branch.open(".")
 
116
 
 
117
 
 
118
    def test_fileid_involved_all_revs(self):
 
119
 
 
120
        l = self.branch.fileid_involved( )
 
121
        self.assertEquals( sorted(map( lambda x: x[0], l )), ["a","b","c","d"])
 
122
 
 
123
    def test_fileid_involved_one_rev(self):
 
124
 
 
125
        l = self.branch.fileid_involved("rev-B" )
 
126
        self.assertEquals( sorted(map( lambda x: x[0], l )), ["a","b","c"])
 
127
 
 
128
    def test_fileid_involved_two_revs(self):
 
129
 
 
130
        l = self.branch.fileid_involved_between_revs("rev-B","rev-K" )
 
131
        self.assertEquals( sorted(map( lambda x: x[0], l )), ["b","c"])
 
132
 
 
133
        l = self.branch.fileid_involved_between_revs("rev-C","rev-<D>" )
 
134
        self.assertEquals( sorted(map( lambda x: x[0], l )), ["b","d"])
 
135
 
 
136
        l = self.branch.fileid_involved_between_revs("rev-C","rev-G" )
 
137
        self.assertEquals( sorted(map( lambda x: x[0], l )), ["b","c","d"])
 
138
 
 
139
        l = self.branch.fileid_involved_between_revs("rev-E","rev-G" )
 
140
        self.assertEquals( sorted(map( lambda x: x[0], l )), ["a", "b","c","d"])
 
141
 
 
142
 
 
143
    def test_fileid_involved_sets(self):
 
144
 
 
145
        l = self.branch.fileid_involved_by_set(set(["rev-B"]))
 
146
        self.assertEquals( sorted(map( lambda x: x[0], l )), ["a"])
 
147
 
 
148
        l = self.branch.fileid_involved_by_set(set(["rev-<D>"]))
 
149
        self.assertEquals( sorted(map( lambda x: x[0], l )), ["b"])
 
150
 
 
151
    def test_fileid_involved_compare(self):
 
152
 
 
153
        l1 = self.branch.fileid_involved_between_revs("rev-E", "rev-<D>")
 
154
        l2 = self.branch.fileid_involved_by_set(set(["rev-<D>","rev-F","rev-C","rev-B"]))
 
155
        self.assertEquals( l1, l2 )
 
156
 
 
157
        l1 = self.branch.fileid_involved_between_revs("rev-C", "rev-G")
 
158
        l2 = self.branch.fileid_involved_by_set(
 
159
            set(["rev-G","rev-<D>","rev-F","rev-K","rev-J"]))
 
160
        self.assertEquals( l1, l2 )
 
161
 
 
162
    def test_fileid_involved_full_compare(self):
 
163
        from bzrlib.tsort import topo_sort
211
164
        pp=[]
212
165
        history = self.branch.revision_history( )
213
166
 
214
167
        if len(history) < 2: return
215
168
 
216
169
        for start in range(0,len(history)-1):
217
 
            start_id = history[start]
218
170
            for end in range(start+1,len(history)):
219
 
                end_id = history[end]
220
 
                old_revs = set(self.branch.repository.get_ancestry(start_id))
221
 
                new_revs = set(self.branch.repository.get_ancestry(end_id))
222
 
                l1 = self.branch.repository.fileids_altered_by_revision_ids(
223
 
                    new_revs.difference(old_revs))
224
 
                l1 = set(l1.keys())
225
 
 
226
 
                l2 = self.compare_tree_fileids(self.branch, start_id, end_id)
227
 
                self.assertEquals(l1, l2)
228
 
 
229
 
 
230
 
class TestFileIdInvolvedNonAscii(FileIdInvolvedBase):
231
 
 
232
 
    def test_utf8_file_ids_and_revision_ids(self):
233
 
        main_wt = self.make_branch_and_tree('main')
234
 
        main_branch = main_wt.branch
235
 
        self.build_tree(["main/a"])
236
 
 
237
 
        file_id = u'a-f\xedle-id'.encode('utf8')
238
 
        main_wt.add(['a'], [file_id])
239
 
        revision_id = u'r\xe9v-a'.encode('utf8')
240
 
        try:
241
 
            main_wt.commit('a', rev_id=revision_id)
242
 
        except errors.NonAsciiRevisionId:
243
 
            raise tests.TestSkipped('non-ascii revision ids not supported by %s'
244
 
                                    % self.repository_format)
245
 
 
246
 
        repo = main_wt.branch.repository
247
 
        repo.lock_read()
248
 
        self.addCleanup(repo.unlock)
249
 
        file_ids = repo.fileids_altered_by_revision_ids([revision_id])
250
 
        root_id = main_wt.basis_tree().get_root_id()
251
 
        if root_id in file_ids:
252
 
            self.assertEqual({file_id:set([revision_id]),
253
 
                              root_id:set([revision_id])
254
 
                             }, file_ids)
255
 
        else:
256
 
            self.assertEqual({file_id:set([revision_id])}, file_ids)
257
 
 
258
 
 
259
 
class TestFileIdInvolvedSuperset(FileIdInvolvedBase):
260
 
 
261
 
    def setUp(self):
262
 
        super(TestFileIdInvolvedSuperset, self).setUp()
263
 
 
264
 
        self.branch = None
265
 
        main_wt = self.make_branch_and_tree('main')
266
 
        main_branch = main_wt.branch
267
 
        self.build_tree(["main/a","main/b","main/c"])
268
 
 
269
 
        main_wt.add(['a', 'b', 'c'], ['a-file-id-2006-01-01-abcd',
270
 
                                 'b-file-id-2006-01-01-defg',
271
 
                                 'c-funky<file-id>quiji\'"%bo'])
272
 
        try:
273
 
            main_wt.commit("Commit one", rev_id="rev-A")
274
 
        except errors.IllegalPath:
275
 
            # TODO: jam 20060701 Consider raising a different exception
276
 
            #       newer formats do support this, and nothin can done to
277
 
            #       correct this test - its not a bug.
278
 
            if sys.platform == 'win32':
279
 
                raise tests.TestSkipped('Old repository formats do not'
280
 
                                        ' support file ids with <> on win32')
281
 
            # This is not a known error condition
282
 
            raise
283
 
 
284
 
        branch2_wt = self.make_branch_and_tree('branch2')
285
 
        branch2_wt.pull(main_branch)
286
 
        branch2_bzrdir = branch2_wt.bzrdir
287
 
        branch2_branch = branch2_bzrdir.open_branch()
288
 
        set_executability(branch2_wt, 'b', True)
289
 
        branch2_wt.commit("branch2, Commit one", rev_id="rev-J")
290
 
 
291
 
        main_wt.merge_from_branch(branch2_branch)
292
 
        set_executability(main_wt, 'b', False)
293
 
        main_wt.commit("merge branch1, rev-22",  rev_id="rev-G")
294
 
 
295
 
        # end G
296
 
        self.branch = main_branch
297
 
 
298
 
    def test_fileid_involved_full_compare2(self):
299
 
        # this tests that fileids_altered_by_revision_ids returns
300
 
        # more information than compare_tree can, because it
301
 
        # sees each change rather than the aggregate delta.
302
 
        self.branch.lock_read()
303
 
        self.addCleanup(self.branch.unlock)
304
 
        history = self.branch.revision_history()
305
 
        old_rev = history[0]
306
 
        new_rev = history[1]
307
 
        old_revs = set(self.branch.repository.get_ancestry(old_rev))
308
 
        new_revs = set(self.branch.repository.get_ancestry(new_rev))
309
 
 
310
 
        l1 = self.branch.repository.fileids_altered_by_revision_ids(
311
 
            new_revs.difference(old_revs))
312
 
        l1 = set(l1.keys())
313
 
 
314
 
        l2 = self.compare_tree_fileids(self.branch, old_rev, new_rev)
315
 
        self.assertNotEqual(l2, l1)
316
 
        self.assertSubset(l2, l1)
317
 
 
318
 
 
319
 
class FileIdInvolvedWGhosts(per_repository.TestCaseWithRepository):
320
 
 
321
 
    def create_branch_with_ghost_text(self):
322
 
        builder = self.make_branch_builder('ghost')
323
 
        builder.build_snapshot('A-id', None, [
324
 
            ('add', ('', 'root-id', 'directory', None)),
325
 
            ('add', ('a', 'a-file-id', 'file', 'some content\n'))])
326
 
        b = builder.get_branch()
327
 
        old_rt = b.repository.revision_tree('A-id')
328
 
        new_inv = old_rt.inventory._get_mutable_inventory()
329
 
        new_inv.revision_id = 'B-id'
330
 
        new_inv['a-file-id'].revision = 'ghost-id'
331
 
        new_rev = _mod_revision.Revision('B-id',
332
 
            timestamp=time.time(),
333
 
            timezone=0,
334
 
            message='Committing against a ghost',
335
 
            committer='Joe Foo <joe@foo.com>',
336
 
            properties={},
337
 
            parent_ids=('A-id', 'ghost-id'),
338
 
            )
339
 
        b.lock_write()
340
 
        self.addCleanup(b.unlock)
341
 
        b.repository.start_write_group()
342
 
        b.repository.add_revision('B-id', new_rev, new_inv)
343
 
        self.disable_commit_write_group_paranoia(b.repository)
344
 
        b.repository.commit_write_group()
345
 
        return b
346
 
 
347
 
    def disable_commit_write_group_paranoia(self, repo):
348
 
        if isinstance(repo, remote.RemoteRepository):
349
 
            # We can't easily disable the checks in a remote repo.
350
 
            repo.abort_write_group()
351
 
            raise tests.TestSkipped(
352
 
                "repository format does not support storing revisions with "
353
 
                "missing texts.")
354
 
        pack_coll = getattr(repo, '_pack_collection', None)
355
 
        if pack_coll is not None:
356
 
            # Monkey-patch the pack collection instance to allow storing
357
 
            # incomplete revisions.
358
 
            pack_coll._check_new_inventories = lambda: []
359
 
 
360
 
    def test_file_ids_include_ghosts(self):
361
 
        b = self.create_branch_with_ghost_text()
362
 
        repo = b.repository
363
 
        self.assertEqual(
364
 
            {'a-file-id':set(['ghost-id'])},
365
 
            repo.fileids_altered_by_revision_ids(['B-id']))
366
 
 
367
 
    def test_file_ids_uses_fallbacks(self):
368
 
        builder = self.make_branch_builder('source',
369
 
                                           format=self.bzrdir_format)
370
 
        repo = builder.get_branch().repository
371
 
        if not repo._format.supports_external_lookups:
372
 
            raise tests.TestNotApplicable('format does not support stacking')
373
 
        builder.start_series()
374
 
        builder.build_snapshot('A-id', None, [
375
 
            ('add', ('', 'root-id', 'directory', None)),
376
 
            ('add', ('file', 'file-id', 'file', 'contents\n'))])
377
 
        builder.build_snapshot('B-id', ['A-id'], [
378
 
            ('modify', ('file-id', 'new-content\n'))])
379
 
        builder.build_snapshot('C-id', ['B-id'], [
380
 
            ('modify', ('file-id', 'yet more content\n'))])
381
 
        builder.finish_series()
382
 
        source_b = builder.get_branch()
383
 
        source_b.lock_read()
384
 
        self.addCleanup(source_b.unlock)
385
 
        base = self.make_branch('base')
386
 
        base.pull(source_b, stop_revision='B-id')
387
 
        stacked = self.make_branch('stacked')
388
 
        stacked.set_stacked_on_url('../base')
389
 
        stacked.pull(source_b, stop_revision='C-id')
390
 
 
391
 
        stacked.lock_read()
392
 
        self.addCleanup(stacked.unlock)
393
 
        repo = stacked.repository
394
 
        keys = {'file-id': set(['A-id'])}
395
 
        if stacked.repository.supports_rich_root():
396
 
            keys['root-id'] = set(['A-id'])
397
 
        self.assertEqual(keys, repo.fileids_altered_by_revision_ids(['A-id']))
398
 
 
399
 
 
400
 
def set_executability(wt, path, executable=True):
401
 
    """Set the executable bit for the file at path in the working tree
402
 
 
403
 
    os.chmod() doesn't work on windows. But TreeTransform can mark or
404
 
    unmark a file as executable.
405
 
    """
406
 
    file_id = wt.path2id(path)
407
 
    tt = transform.TreeTransform(wt)
408
 
    try:
409
 
        tt.set_executability(executable, tt.trans_id_tree_file_id(file_id))
410
 
        tt.apply()
411
 
    finally:
412
 
        tt.finalize()
 
171
 
 
172
                l1 = self.branch.fileid_involved_between_revs(
 
173
                    history[start], history[end])
 
174
 
 
175
                old_tree = self.branch.revision_tree(history[start])
 
176
                new_tree = self.branch.revision_tree(history[end])
 
177
                delta = compare_trees(old_tree, new_tree )
 
178
 
 
179
                l2 = [ id for path, id, kind in delta.added ] + \
 
180
                     [ id for oldpath, newpath, id, kind, text_modified, \
 
181
                            meta_modified in delta.renamed ] + \
 
182
                     [ id for path, id, kind, text_modified, meta_modified in \
 
183
                            delta.modified ]
 
184
 
 
185
                self.assertEquals( l1, set(l2) )
 
186