/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
0.264.10 by Jelmer Vernooij
Yield inventory entries.
1
# Copyright (C) 2008-2011 Jelmer Vernooij <jelmer@samba.org>
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
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
0.200.381 by Jelmer Vernooij
Support working trees properly, status and ls.
17
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
18
"""An adapter between a Git index and a Bazaar Working Tree"""
19
0.200.1594 by Jelmer Vernooij
Use absolute_import everywhere.
20
from __future__ import absolute_import
0.200.381 by Jelmer Vernooij
Support working trees properly, status and ls.
21
0.200.1731 by Jelmer Vernooij
Add support for checking untracked changes.
22
import itertools
0.200.385 by Jelmer Vernooij
Cope with removed files.
23
from cStringIO import (
24
    StringIO,
25
    )
0.200.1210 by Jelmer Vernooij
Implement GitWorkingTree._walkdirs.
26
from collections import defaultdict
0.239.4 by Jelmer Vernooij
Cope with nonexistent files and directories in get_file_sha1.
27
import errno
0.200.1538 by Jelmer Vernooij
More work on tree-reference support.
28
from dulwich.errors import NotGitRepository
0.200.1655 by Jelmer Vernooij
Basic support for git ignores.
29
from dulwich.ignore import (
0.200.1658 by Jelmer Vernooij
Fix handling of ignores - return patterns that matched.
30
    IgnoreFilterManager,
0.200.1655 by Jelmer Vernooij
Basic support for git ignores.
31
    )
0.200.1096 by Jelmer Vernooij
Implement GitWorkingTreeFormat.initialize.
32
from dulwich.index import (
33
    Index,
0.200.1531 by Jelmer Vernooij
Don't trust index contents - verify against file timestamps.
34
    changes_from_tree,
35
    cleanup_mode,
0.200.1754 by Jelmer Vernooij
Avoid updating index when just comparing trees.
36
    commit_tree,
0.200.1531 by Jelmer Vernooij
Don't trust index contents - verify against file timestamps.
37
    index_entry_from_stat,
0.200.1754 by Jelmer Vernooij
Avoid updating index when just comparing trees.
38
    iter_fresh_blobs,
0.323.1 by Jelmer Vernooij
Fix iter_changes of untracked changes.
39
    blob_from_path_and_stat,
0.200.1096 by Jelmer Vernooij
Implement GitWorkingTreeFormat.initialize.
40
    )
0.200.1202 by Jelmer Vernooij
Implement has_or_had_id.
41
from dulwich.object_store import (
42
    tree_lookup_path,
43
    )
0.200.383 by Jelmer Vernooij
Simplify, support rewriting index based on inventory.
44
from dulwich.objects import (
45
    Blob,
0.200.1538 by Jelmer Vernooij
More work on tree-reference support.
46
    S_IFGITLINK,
0.200.948 by Jelmer Vernooij
Cope with empty inventories.
47
    )
0.200.1538 by Jelmer Vernooij
More work on tree-reference support.
48
from dulwich.repo import Repo
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
49
import os
0.264.10 by Jelmer Vernooij
Yield inventory entries.
50
import posixpath
0.200.1655 by Jelmer Vernooij
Basic support for git ignores.
51
import re
0.200.384 by Jelmer Vernooij
Fix reading of inventory from index.
52
import stat
0.200.1215 by Jelmer Vernooij
Implement GitWorkingTree.remove.
53
import sys
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
54
0.200.1641 by Jelmer Vernooij
Use relative imports where possible.
55
from ... import (
0.200.382 by Jelmer Vernooij
Support flushing index.
56
    errors,
0.262.1 by Jelmer Vernooij
Fix WorkingTree.conflicts().
57
    conflicts as _mod_conflicts,
0.200.1752 by Jelmer Vernooij
Don't traverse nested trees in WorkingTree.smart_add.
58
    controldir as _mod_controldir,
0.200.1655 by Jelmer Vernooij
Basic support for git ignores.
59
    globbing,
0.200.409 by Jelmer Vernooij
Support parsing .gitignore.
60
    ignores,
0.200.1476 by Jelmer Vernooij
Cope with working tree refactoring.
61
    lock,
0.200.381 by Jelmer Vernooij
Support working trees properly, status and ls.
62
    osutils,
0.200.1720 by Jelmer Vernooij
Fix handling of pending merges.
63
    revision as _mod_revision,
0.200.1215 by Jelmer Vernooij
Implement GitWorkingTree.remove.
64
    trace,
0.200.1752 by Jelmer Vernooij
Don't traverse nested trees in WorkingTree.smart_add.
65
    transport as _mod_transport,
0.200.519 by Jelmer Vernooij
Move imports down, might not be available in older bzr-git versions.
66
    tree,
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
67
    workingtree,
68
    )
0.200.1648 by Jelmer Vernooij
Fix compatibility with newer versions of breezy.
69
from ...bzr import (
70
    inventory,
71
    )
0.200.1680 by Jelmer Vernooij
Fix repo locks.
72
from ...mutabletree import (
73
    MutableTree,
74
    )
0.200.1641 by Jelmer Vernooij
Use relative imports where possible.
75
76
77
from .dir import (
0.200.1096 by Jelmer Vernooij
Implement GitWorkingTreeFormat.initialize.
78
    LocalGitDir,
79
    )
0.200.1641 by Jelmer Vernooij
Use relative imports where possible.
80
from .tree import (
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
81
    changes_from_git_changes,
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
82
    tree_delta_from_git_changes,
0.287.3 by Jelmer Vernooij
Some improvements to changes iterator.
83
    InterGitTrees,
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
84
    )
0.200.1641 by Jelmer Vernooij
Use relative imports where possible.
85
from .mapping import (
0.200.971 by Chadrik
Fix 'bzr status' after 'bzr add' in native git working trees.
86
    GitFileIdMap,
0.264.10 by Jelmer Vernooij
Yield inventory entries.
87
    mode_kind,
0.200.971 by Chadrik
Fix 'bzr status' after 'bzr add' in native git working trees.
88
    )
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
89
0.200.409 by Jelmer Vernooij
Support parsing .gitignore.
90
IGNORE_FILENAME = ".gitignore"
91
92
0.200.1768 by Jelmer Vernooij
Fix handling of normalized filenames.
93
def ensure_normalized_path(path):
94
    """Check whether path is normalized.
95
96
    :raises InvalidNormalization: When path is not normalized, and cannot be
97
        accessed on this platform by the normalized path.
98
    :return: The NFC normalised version of path.
99
    """
100
    norm_path, can_access = osutils.normalized_filename(path)
101
    if norm_path != path:
102
        if can_access:
103
            return norm_path
104
        else:
105
            raise errors.InvalidNormalization(path)
106
    return path
107
108
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
109
class GitWorkingTree(workingtree.WorkingTree):
110
    """A Git working tree."""
111
0.200.1648 by Jelmer Vernooij
Fix compatibility with newer versions of breezy.
112
    def __init__(self, controldir, repo, branch, index):
0.200.1741 by Jelmer Vernooij
Fix opentree tests.
113
        basedir = controldir.root_transport.local_abspath('.')
114
        self.basedir = osutils.realpath(basedir)
0.200.1648 by Jelmer Vernooij
Fix compatibility with newer versions of breezy.
115
        self.controldir = controldir
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
116
        self.repository = repo
0.200.1205 by Jelmer Vernooij
Implement GitWorkingTree.stored_kind.
117
        self.store = self.repository._git.object_store
0.200.384 by Jelmer Vernooij
Fix reading of inventory from index.
118
        self.mapping = self.repository.get_mapping()
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
119
        self._branch = branch
0.200.1648 by Jelmer Vernooij
Fix compatibility with newer versions of breezy.
120
        self._transport = controldir.transport
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
121
        self._format = GitWorkingTreeFormat()
0.200.803 by Jelmer Vernooij
Default to non-bare repositories when initializing a control directory.
122
        self.index = index
0.200.1242 by Jelmer Vernooij
Support directories better.
123
        self._versioned_dirs = None
0.200.239 by Jelmer Vernooij
Provide views.
124
        self.views = self._make_views()
0.200.1173 by Jelmer Vernooij
Provide GitWorkingTree._rules_searcher.
125
        self._rules_searcher = None
0.200.381 by Jelmer Vernooij
Support working trees properly, status and ls.
126
        self._detect_case_handling()
0.200.1202 by Jelmer Vernooij
Implement has_or_had_id.
127
        self._reset_data()
128
        self._fileid_map = self._basis_fileid_map.copy()
0.200.1476 by Jelmer Vernooij
Cope with working tree refactoring.
129
        self._lock_mode = None
130
        self._lock_count = 0
131
0.200.1650 by Jelmer Vernooij
Implement GitWorkingTree.supports_tree_reference.
132
    def supports_tree_reference(self):
133
        return False
134
0.200.1476 by Jelmer Vernooij
Cope with working tree refactoring.
135
    def lock_read(self):
136
        """Lock the repository for read operations.
137
0.200.1646 by Jelmer Vernooij
Rename bzrlib to breezy.
138
        :return: A breezy.lock.LogicalLockResult.
0.200.1476 by Jelmer Vernooij
Cope with working tree refactoring.
139
        """
140
        if not self._lock_mode:
141
            self._lock_mode = 'r'
142
            self._lock_count = 1
0.200.1525 by Jelmer Vernooij
Make sure to always use an up-to-date index.
143
            self.index.read()
0.200.1476 by Jelmer Vernooij
Cope with working tree refactoring.
144
        else:
145
            self._lock_count += 1
146
        self.branch.lock_read()
147
        return lock.LogicalLockResult(self.unlock)
148
0.200.1477 by Jelmer Vernooij
Implement GitWorkingTree.lock_tree_write.
149
    def lock_tree_write(self):
150
        if not self._lock_mode:
151
            self._lock_mode = 'w'
152
            self._lock_count = 1
0.200.1525 by Jelmer Vernooij
Make sure to always use an up-to-date index.
153
            self.index.read()
0.200.1477 by Jelmer Vernooij
Implement GitWorkingTree.lock_tree_write.
154
        elif self._lock_mode == 'r':
155
            raise errors.ReadOnlyError(self)
156
        else:
157
            self._lock_count +=1
158
        self.branch.lock_read()
159
        return lock.LogicalLockResult(self.unlock)
160
0.200.1476 by Jelmer Vernooij
Cope with working tree refactoring.
161
    def lock_write(self, token=None):
162
        if not self._lock_mode:
163
            self._lock_mode = 'w'
164
            self._lock_count = 1
0.200.1525 by Jelmer Vernooij
Make sure to always use an up-to-date index.
165
            self.index.read()
0.200.1476 by Jelmer Vernooij
Cope with working tree refactoring.
166
        elif self._lock_mode == 'r':
167
            raise errors.ReadOnlyError(self)
168
        else:
169
            self._lock_count +=1
170
        self.branch.lock_write()
171
        return lock.LogicalLockResult(self.unlock)
172
173
    def is_locked(self):
174
        return self._lock_count >= 1
175
176
    def get_physical_lock_status(self):
177
        return False
178
179
    def unlock(self):
180
        if not self._lock_count:
181
            return lock.cant_unlock_not_held(self)
0.200.1530 by Jelmer Vernooij
Fix lock order.
182
        self.branch.unlock()
0.200.1476 by Jelmer Vernooij
Cope with working tree refactoring.
183
        self._cleanup()
184
        self._lock_count -= 1
185
        if self._lock_count > 0:
186
            return
187
        self._lock_mode = None
0.200.173 by Jelmer Vernooij
Merge changes, open index.
188
0.200.1658 by Jelmer Vernooij
Fix handling of ignores - return patterns that matched.
189
    def _cleanup(self):
190
        pass
191
0.200.1322 by Jelmer Vernooij
Add case detection.
192
    def _detect_case_handling(self):
193
        try:
194
            self._transport.stat(".git/cOnFiG")
195
        except errors.NoSuchFile:
196
            self.case_sensitive = True
197
        else:
198
            self.case_sensitive = False
199
0.200.1315 by Jelmer Vernooij
Implement WorkingTree.merge_modified.
200
    def merge_modified(self):
201
        return {}
202
0.200.1696 by Jelmer Vernooij
Fix set_merge_modified.
203
    def set_merge_modified(self, modified_hashes):
0.298.1 by Jelmer Vernooij
mark GitWorkngTree.set_merge_modified as unsupported.
204
        raise errors.UnsupportedOperation(self.set_merge_modified, self)
0.200.1690 by Jelmer Vernooij
Implement WorkingTree.set_merge_modified.
205
0.200.1220 by Jelmer Vernooij
Support set_parent_trees.
206
    def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
207
        self.set_parent_ids([p for p, t in parents_list])
208
0.305.1 by Jelmer Vernooij
Reset RHS parents after commit.
209
    def _set_merges_from_parent_ids(self, rhs_parent_ids):
0.200.1779 by Jelmer Vernooij
Actually store git revision ids rather than bzr revision ids.
210
        try:
0.305.1 by Jelmer Vernooij
Reset RHS parents after commit.
211
            merges = [self.branch.lookup_bzr_revision_id(revid)[0] for revid in rhs_parent_ids]
0.200.1779 by Jelmer Vernooij
Actually store git revision ids rather than bzr revision ids.
212
        except errors.NoSuchRevision as e:
213
            raise errors.GhostRevisionUnusableHere(e.revision)
0.304.1 by Jelmer Vernooij
Delete MERGE_HEAD if there are no parent ids.
214
        if merges:
215
            self.control_transport.put_bytes('MERGE_HEAD', '\n'.join(merges),
216
                mode=self.controldir._get_file_mode())
217
        else:
218
            try:
219
                self.control_transport.delete('MERGE_HEAD')
220
            except errors.NoSuchFile:
221
                pass
0.200.1720 by Jelmer Vernooij
Fix handling of pending merges.
222
223
    def set_parent_ids(self, revision_ids, allow_leftmost_as_ghost=False):
224
        """Set the parent ids to revision_ids.
225
226
        See also set_parent_trees. This api will try to retrieve the tree data
227
        for each element of revision_ids from the trees repository. If you have
228
        tree data already available, it is more efficient to use
229
        set_parent_trees rather than set_parent_ids. set_parent_ids is however
230
        an easier API to use.
231
232
        :param revision_ids: The revision_ids to set as the parent ids of this
233
            working tree. Any of these may be ghosts.
234
        """
235
        with self.lock_tree_write():
236
            self._check_parents_for_ghosts(revision_ids,
237
                allow_leftmost_as_ghost=allow_leftmost_as_ghost)
238
            for revision_id in revision_ids:
239
                _mod_revision.check_not_reserved_id(revision_id)
240
241
            revision_ids = self._filter_parent_ids_by_ancestry(revision_ids)
242
243
            if len(revision_ids) > 0:
244
                self.set_last_revision(revision_ids[0])
245
            else:
246
                self.set_last_revision(_mod_revision.NULL_REVISION)
247
0.305.1 by Jelmer Vernooij
Reset RHS parents after commit.
248
            self._set_merges_from_parent_ids(revision_ids[1:])
0.200.1720 by Jelmer Vernooij
Fix handling of pending merges.
249
250
    def get_parent_ids(self):
251
        """See Tree.get_parent_ids.
252
253
        This implementation reads the pending merges list and last_revision
254
        value and uses that to decide what the parents list should be.
255
        """
256
        last_rev = _mod_revision.ensure_null(self._last_revision())
257
        if _mod_revision.NULL_REVISION == last_rev:
258
            parents = []
259
        else:
260
            parents = [last_rev]
261
        try:
262
            merges_bytes = self.control_transport.get_bytes('MERGE_HEAD')
263
        except errors.NoSuchFile:
264
            pass
265
        else:
266
            for l in osutils.split_lines(merges_bytes):
267
                revision_id = l.rstrip('\n')
0.200.1779 by Jelmer Vernooij
Actually store git revision ids rather than bzr revision ids.
268
                parents.append(self.branch.lookup_foreign_revision_id(revision_id))
0.200.1720 by Jelmer Vernooij
Fix handling of pending merges.
269
        return parents
270
0.200.1599 by Jelmer Vernooij
Implement GitWorkingTree.iter_children.
271
    def iter_children(self, file_id):
272
        dpath = self.id2path(file_id) + "/"
273
        if dpath in self.index:
274
            return
275
        for path in self.index:
276
            if not path.startswith(dpath):
277
                continue
278
            if "/" in path[len(dpath):]:
279
                # Not a direct child but something further down
280
                continue
281
            yield self.path2id(path)
282
0.316.2 by Jelmer Vernooij
Flush after modifying index.
283
    def _index_add_entry(self, path, kind, flags=0):
0.200.1525 by Jelmer Vernooij
Make sure to always use an up-to-date index.
284
        assert self._lock_mode is not None
0.200.1206 by Jelmer Vernooij
Implement GitWorkingTree.all_file_ids.
285
        assert isinstance(path, basestring)
0.264.2 by Jelmer Vernooij
Implement GitWorkingTree.{_add,__iter__,id2path}.
286
        if kind == "directory":
287
            # Git indexes don't contain directories
288
            return
289
        if kind == "file":
290
            blob = Blob()
291
            try:
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
292
                file, stat_val = self.get_file_with_stat(path)
0.264.2 by Jelmer Vernooij
Implement GitWorkingTree.{_add,__iter__,id2path}.
293
            except (errors.NoSuchFile, IOError):
294
                # TODO: Rather than come up with something here, use the old index
295
                file = StringIO()
0.265.1 by Martin
Don't import posix module, the os wrapper exists for portability
296
                stat_val = os.stat_result(
297
                    (stat.S_IFREG | 0644, 0, 0, 0, 0, 0, 0, 0, 0, 0))
0.264.2 by Jelmer Vernooij
Implement GitWorkingTree.{_add,__iter__,id2path}.
298
            blob.set_raw_string(file.read())
299
        elif kind == "symlink":
300
            blob = Blob()
301
            try:
302
                stat_val = os.lstat(self.abspath(path))
303
            except (errors.NoSuchFile, OSError):
0.200.1636 by Jelmer Vernooij
Some formatting fixes.
304
                # TODO: Rather than come up with something here, use the
0.264.2 by Jelmer Vernooij
Implement GitWorkingTree.{_add,__iter__,id2path}.
305
                # old index
0.265.1 by Martin
Don't import posix module, the os wrapper exists for portability
306
                stat_val = os.stat_result(
307
                    (stat.S_IFLNK, 0, 0, 0, 0, 0, 0, 0, 0, 0))
0.200.1321 by Jelmer Vernooij
More fixes for compatibility with bzr.dev testsuite.
308
            blob.set_raw_string(
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
309
                self.get_symlink_target(path).encode("utf-8"))
0.264.2 by Jelmer Vernooij
Implement GitWorkingTree.{_add,__iter__,id2path}.
310
        else:
311
            raise AssertionError("unknown kind '%s'" % kind)
312
        # Add object to the repository if it didn't exist yet
0.200.1205 by Jelmer Vernooij
Implement GitWorkingTree.stored_kind.
313
        if not blob.id in self.store:
314
            self.store.add_object(blob)
0.264.2 by Jelmer Vernooij
Implement GitWorkingTree.{_add,__iter__,id2path}.
315
        # Add an entry to the index or update the existing entry
0.200.1768 by Jelmer Vernooij
Fix handling of normalized filenames.
316
        ensure_normalized_path(path)
0.200.1242 by Jelmer Vernooij
Support directories better.
317
        encoded_path = path.encode("utf-8")
0.200.1770 by Jelmer Vernooij
Fix test for illegal characters in filenames.
318
        if b'\r' in encoded_path or b'\n' in encoded_path:
319
            # TODO(jelmer): Why do we need to do this?
320
            trace.mutter('ignoring path with invalid newline in it: %r', path)
321
            return
0.200.1531 by Jelmer Vernooij
Don't trust index contents - verify against file timestamps.
322
        self.index[encoded_path] = index_entry_from_stat(
323
            stat_val, blob.id, flags)
0.200.1242 by Jelmer Vernooij
Support directories better.
324
        if self._versioned_dirs is not None:
325
            self._ensure_versioned_dir(encoded_path)
326
327
    def _ensure_versioned_dir(self, dirname):
0.200.1249 by Jelmer Vernooij
Fix file id for tree root
328
        if dirname in self._versioned_dirs:
0.200.1242 by Jelmer Vernooij
Support directories better.
329
            return
0.200.1249 by Jelmer Vernooij
Fix file id for tree root
330
        if dirname != "":
331
            self._ensure_versioned_dir(posixpath.dirname(dirname))
0.200.1242 by Jelmer Vernooij
Support directories better.
332
        self._versioned_dirs.add(dirname)
333
334
    def _load_dirs(self):
0.200.1525 by Jelmer Vernooij
Make sure to always use an up-to-date index.
335
        assert self._lock_mode is not None
0.200.1242 by Jelmer Vernooij
Support directories better.
336
        self._versioned_dirs = set()
337
        for p in self.index:
338
            self._ensure_versioned_dir(posixpath.dirname(p))
0.264.2 by Jelmer Vernooij
Implement GitWorkingTree.{_add,__iter__,id2path}.
339
0.200.1215 by Jelmer Vernooij
Implement GitWorkingTree.remove.
340
    def _unversion_path(self, path):
0.200.1525 by Jelmer Vernooij
Make sure to always use an up-to-date index.
341
        assert self._lock_mode is not None
0.200.1215 by Jelmer Vernooij
Implement GitWorkingTree.remove.
342
        encoded_path = path.encode("utf-8")
0.287.6 by Jelmer Vernooij
Fix some more tests.
343
        count = 0
0.200.1215 by Jelmer Vernooij
Implement GitWorkingTree.remove.
344
        try:
345
            del self.index[encoded_path]
346
        except KeyError:
347
            # A directory, perhaps?
348
            for p in list(self.index):
0.200.1692 by Jelmer Vernooij
Mark three more tests as xfail.
349
                if p.startswith(encoded_path+b"/"):
0.287.6 by Jelmer Vernooij
Fix some more tests.
350
                    count += 1
0.200.1215 by Jelmer Vernooij
Implement GitWorkingTree.remove.
351
                    del self.index[p]
0.287.6 by Jelmer Vernooij
Fix some more tests.
352
        else:
353
            count = 1
0.200.1772 by Jelmer Vernooij
Fix handling of unversioned file in get_file_sha1.
354
        self._versioned_dirs = None
0.287.6 by Jelmer Vernooij
Fix some more tests.
355
        return count
0.200.1215 by Jelmer Vernooij
Implement GitWorkingTree.remove.
356
0.285.8 by Jelmer Vernooij
Fix more tests for swapped arguments.
357
    def unversion(self, paths, file_ids=None):
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
358
        with self.lock_tree_write():
0.200.1690 by Jelmer Vernooij
Implement WorkingTree.set_merge_modified.
359
            for path in paths:
0.200.1742 by Jelmer Vernooij
Fix some unversion tests.
360
                encoded_path = path.encode("utf-8")
361
                try:
362
                    del self.index[encoded_path]
363
                except KeyError:
364
                    if not self._has_dir(path):
365
                        raise errors.NoSuchFile(path)
0.200.1772 by Jelmer Vernooij
Fix handling of unversioned file in get_file_sha1.
366
            self._versioned_dirs = None
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
367
            self.flush()
0.200.1215 by Jelmer Vernooij
Implement GitWorkingTree.remove.
368
0.200.1678 by Jelmer Vernooij
Fix tests.
369
    def update_basis_by_delta(self, revid, delta):
370
        # TODO(jelmer): This shouldn't be called, it's inventory specific.
0.316.2 by Jelmer Vernooij
Flush after modifying index.
371
        for (old_path, new_path, file_id, ie) in delta:
372
            if old_path is not None and old_path.encode('utf-8') in self.index:
373
                del self.index[old_path.encode('utf-8')]
374
                self._versioned_dirs = None
375
            if new_path is not None and ie.kind != 'directory':
376
                self._index_add_entry(new_path, ie.kind)
377
        self.flush()
378
        self._set_merges_from_parent_ids([])
0.200.1678 by Jelmer Vernooij
Fix tests.
379
0.200.1243 by Jelmer Vernooij
Implement WorkingTree.check_state.
380
    def check_state(self):
381
        """Check that the working state is/isn't valid."""
382
        pass
383
0.200.1215 by Jelmer Vernooij
Implement GitWorkingTree.remove.
384
    def remove(self, files, verbose=False, to_file=None, keep_files=True,
385
        force=False):
386
        """Remove nominated files from the working tree metadata.
387
388
        :param files: File paths relative to the basedir.
389
        :param keep_files: If true, the files will also be kept.
390
        :param force: Delete files and directories, even if they are changed
391
            and even if the directories are not empty.
392
        """
393
        if isinstance(files, basestring):
394
            files = [files]
395
396
        if to_file is None:
397
            to_file = sys.stdout
398
0.200.1735 by Jelmer Vernooij
Fix remove tests.
399
        files = list(files)
0.200.1215 by Jelmer Vernooij
Implement GitWorkingTree.remove.
400
401
        if len(files) == 0:
402
            return # nothing to do
403
404
        # Sort needed to first handle directory content before the directory
405
        files.sort(reverse=True)
406
407
        def backup(file_to_backup):
408
            abs_path = self.abspath(file_to_backup)
0.200.1648 by Jelmer Vernooij
Fix compatibility with newer versions of breezy.
409
            backup_name = self.controldir._available_backup_name(file_to_backup)
0.200.1215 by Jelmer Vernooij
Implement GitWorkingTree.remove.
410
            osutils.rename(abs_path, self.abspath(backup_name))
411
            return "removed %s (but kept a copy: %s)" % (
412
                file_to_backup, backup_name)
413
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
414
        with self.lock_tree_write():
415
            for f in files:
0.200.1735 by Jelmer Vernooij
Fix remove tests.
416
                if f == '':
417
                    continue
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
418
                else:
419
                    abs_path = self.abspath(f)
420
                    if verbose:
421
                        # having removed it, it must be either ignored or unknown
422
                        if self.is_ignored(f):
423
                            new_status = 'I'
424
                        else:
425
                            new_status = '?'
426
                        # XXX: Really should be a more abstract reporter interface
0.287.6 by Jelmer Vernooij
Fix some more tests.
427
                        kind_ch = osutils.kind_marker(self.kind(f))
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
428
                        to_file.write(new_status + '       ' + f + kind_ch + '\n')
429
                    # Unversion file
0.287.6 by Jelmer Vernooij
Fix some more tests.
430
                    # TODO(jelmer): _unversion_path() is O(size-of-index) for directories
431
                    if self._unversion_path(f) == 0:
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
432
                        if (osutils.isdir(abs_path) and
0.287.6 by Jelmer Vernooij
Fix some more tests.
433
                            len(os.listdir(abs_path)) == 0):
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
434
                            if not keep_files:
435
                                osutils.delete_any(abs_path)
0.287.6 by Jelmer Vernooij
Fix some more tests.
436
                            message = "removed %s" % (f,)
437
                        else:
438
                            message = "%s is not versioned." % (f,)
439
                    else:
440
                        message = "removed %s" % (f,)
441
                        if osutils.lexists(abs_path):
442
                            if (osutils.isdir(abs_path) and
443
                                len(os.listdir(abs_path)) > 0):
444
                                if force:
445
                                    osutils.rmtree(abs_path)
446
                                    message = "deleted %s" % (f,)
447
                                else:
448
                                    message = backup(f)
449
                            else:
450
                                if not keep_files:
451
                                    osutils.delete_any(abs_path)
452
                                    message = "deleted %s" % (f,)
0.200.1215 by Jelmer Vernooij
Implement GitWorkingTree.remove.
453
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
454
                # print only one message (if any) per file.
455
                if message is not None:
456
                    trace.note(message)
457
            self.flush()
0.200.1192 by Jelmer Vernooij
Implement path2id.
458
0.264.2 by Jelmer Vernooij
Implement GitWorkingTree.{_add,__iter__,id2path}.
459
    def _add(self, files, ids, kinds):
460
        for (path, file_id, kind) in zip(files, ids, kinds):
0.200.1201 by Jelmer Vernooij
Implement _set_root_id.
461
            if file_id is not None:
0.200.1663 by Jelmer Vernooij
Raise SettingFileIdUnsupported
462
                raise workingtree.SettingFileIdUnsupported()
463
            self._index_add_entry(path, kind)
0.200.1201 by Jelmer Vernooij
Implement _set_root_id.
464
0.200.1240 by Jelmer Vernooij
Implement GitWorkingTree.smart_add.
465
    def smart_add(self, file_list, recurse=True, action=None, save=True):
0.200.1771 by Jelmer Vernooij
Fix implicit tree unicode add test.
466
        if not file_list:
467
            file_list = [u'.']
0.200.1781 by Jelmer Vernooij
Support expanding symlinks.
468
469
        # expand any symlinks in the directory part, while leaving the
470
        # filename alone
471
        # only expanding if symlinks are supported avoids windows path bugs
472
        if osutils.has_symlinks():
473
            file_list = list(map(osutils.normalizepath, file_list))
474
0.200.1240 by Jelmer Vernooij
Implement GitWorkingTree.smart_add.
475
        added = []
476
        ignored = {}
477
        user_dirs = []
0.200.1733 by Jelmer Vernooij
Support handling of custom ids in smart_add.
478
        def call_action(filepath, kind):
479
            if action is not None:
480
                parent_path = posixpath.dirname(filepath)
481
                parent_id = self.path2id(parent_path)
482
                parent_ie = self._get_dir_ie(parent_path, parent_id)
483
                file_id = action(self, parent_ie, filepath, kind)
484
                if file_id is not None:
485
                    raise workingtree.SettingFileIdUnsupported()
486
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
487
        with self.lock_tree_write():
488
            for filepath in osutils.canonical_relpaths(self.basedir, file_list):
0.200.1768 by Jelmer Vernooij
Fix handling of normalized filenames.
489
                filepath = osutils.normalized_filename(filepath)[0]
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
490
                abspath = self.abspath(filepath)
0.200.1240 by Jelmer Vernooij
Implement GitWorkingTree.smart_add.
491
                kind = osutils.file_kind(abspath)
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
492
                if kind in ("file", "symlink"):
0.200.1733 by Jelmer Vernooij
Support handling of custom ids in smart_add.
493
                    call_action(filepath, kind)
0.200.1308 by Jelmer Vernooij
Write index to disk after adding files.
494
                    if save:
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
495
                        self._index_add_entry(filepath, kind)
496
                    added.append(filepath)
497
                elif kind == "directory":
0.200.1733 by Jelmer Vernooij
Support handling of custom ids in smart_add.
498
                    call_action(filepath, kind)
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
499
                    if recurse:
500
                        user_dirs.append(filepath)
501
                else:
502
                    raise errors.BadFileKindError(filename=abspath, kind=kind)
503
            for user_dir in user_dirs:
504
                abs_user_dir = self.abspath(user_dir)
0.200.1752 by Jelmer Vernooij
Don't traverse nested trees in WorkingTree.smart_add.
505
                if user_dir != '':
506
                    try:
507
                        transport = _mod_transport.get_transport_from_path(abs_user_dir)
508
                        _mod_controldir.ControlDirFormat.find_format(transport)
509
                        subtree = True
510
                    except errors.NotBranchError:
511
                        subtree = False
512
                    except errors.UnsupportedFormatError:
513
                        subtree = False
514
                else:
515
                    subtree = False
516
                if subtree:
0.200.1769 by Jelmer Vernooij
Raise proper error when encountering nested trees.
517
                    trace.warning('skipping nested tree %r', abs_user_dir)
0.200.1752 by Jelmer Vernooij
Don't traverse nested trees in WorkingTree.smart_add.
518
                    continue
519
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
520
                for name in os.listdir(abs_user_dir):
521
                    subp = os.path.join(user_dir, name)
522
                    if self.is_control_filename(subp) or self.mapping.is_special_file(subp):
523
                        continue
524
                    ignore_glob = self.is_ignored(subp)
525
                    if ignore_glob is not None:
526
                        ignored.setdefault(ignore_glob, []).append(subp)
527
                        continue
528
                    abspath = self.abspath(subp)
529
                    kind = osutils.file_kind(abspath)
530
                    if kind == "directory":
531
                        user_dirs.append(subp)
532
                    else:
0.200.1733 by Jelmer Vernooij
Support handling of custom ids in smart_add.
533
                        call_action(filepath, kind)
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
534
                        if save:
535
                            self._index_add_entry(subp, kind)
0.200.1751 by Jelmer Vernooij
Fix smart_add return value.
536
                        added.append(subp)
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
537
            if added and save:
538
                self.flush()
539
            return added, ignored
0.200.1240 by Jelmer Vernooij
Implement GitWorkingTree.smart_add.
540
0.200.1201 by Jelmer Vernooij
Implement _set_root_id.
541
    def _set_root_id(self, file_id):
542
        self._fileid_map.set_file_id("", file_id)
0.264.2 by Jelmer Vernooij
Implement GitWorkingTree.{_add,__iter__,id2path}.
543
0.347.5 by Jelmer Vernooij
Fix more move/rename errors.
544
    def move(self, from_paths, to_dir=None, after=None):
0.200.1193 by Jelmer Vernooij
Implement GitWorkingTree.{move,rename_one}.
545
        rename_tuples = []
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
546
        with self.lock_tree_write():
547
            to_abs = self.abspath(to_dir)
548
            if not os.path.isdir(to_abs):
549
                raise errors.BzrMoveFailedError('', to_dir,
550
                    errors.NotADirectory(to_abs))
551
552
            for from_rel in from_paths:
553
                from_tail = os.path.split(from_rel)[-1]
554
                to_rel = os.path.join(to_dir, from_tail)
555
                self.rename_one(from_rel, to_rel, after=after)
556
                rename_tuples.append((from_rel, to_rel))
557
            self.flush()
558
            return rename_tuples
559
0.347.5 by Jelmer Vernooij
Fix more move/rename errors.
560
    def rename_one(self, from_rel, to_rel, after=None):
0.200.1203 by Jelmer Vernooij
Fix per_workingtree.test_rename_one.TestRenameOne.test_rename_after_non_existant_non_ascii
561
        from_path = from_rel.encode("utf-8")
0.200.1768 by Jelmer Vernooij
Fix handling of normalized filenames.
562
        ensure_normalized_path(to_rel)
0.200.1203 by Jelmer Vernooij
Fix per_workingtree.test_rename_one.TestRenameOne.test_rename_after_non_existant_non_ascii
563
        to_path = to_rel.encode("utf-8")
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
564
        with self.lock_tree_write():
0.347.5 by Jelmer Vernooij
Fix more move/rename errors.
565
            if after is None:
566
                # Perhaps it's already moved?
567
                after = (
568
                    not self.has_filename(from_rel) and
569
                    self.has_filename(to_rel) and
570
                    self.is_versioned(from_rel) and
571
                    not self.is_versioned(to_rel))
572
            if after:
573
                if not self.has_filename(to_rel):
574
                    raise errors.BzrMoveFailedError(from_rel, to_rel,
575
                        errors.NoSuchFile(to_rel))
576
                if self.basis_tree().is_versioned(to_rel):
577
                    raise errors.BzrMoveFailedError(from_rel, to_rel,
578
                        errors.AlreadyVersionedError(to_rel))
579
580
                kind = self.kind(to_rel)
581
            else:
0.347.1 by Jelmer Vernooij
Various rename fixes.
582
                try:
583
                    to_kind = self.kind(to_rel)
584
                except errors.NoSuchFile:
585
                    exc_type = errors.BzrRenameFailedError
0.347.2 by Jelmer Vernooij
Fix breezy.tests.per_workingtree.test_rename_one.TestRenameOne.test_rename_one_fail_consistent
586
                    to_kind = None
0.347.1 by Jelmer Vernooij
Various rename fixes.
587
                else:
588
                    exc_type = errors.BzrMoveFailedError
0.347.5 by Jelmer Vernooij
Fix more move/rename errors.
589
                if self.is_versioned(to_rel):
590
                    raise exc_type(from_rel, to_rel,
591
                        errors.AlreadyVersionedError(to_rel))
0.200.1737 by Jelmer Vernooij
Fix rename tests.
592
                if not self.has_filename(from_rel):
593
                    raise errors.BzrMoveFailedError(from_rel, to_rel,
594
                        errors.NoSuchFile(from_rel))
0.347.1 by Jelmer Vernooij
Various rename fixes.
595
                if not self.is_versioned(from_rel):
596
                    raise exc_type(from_rel, to_rel,
597
                        errors.NotVersionedError(from_rel))
0.347.2 by Jelmer Vernooij
Fix breezy.tests.per_workingtree.test_rename_one.TestRenameOne.test_rename_one_fail_consistent
598
                if self.has_filename(to_rel):
599
                    raise errors.RenameFailedFilesExist(
600
                        from_rel, to_rel, errors.FileExists(to_rel))
0.347.5 by Jelmer Vernooij
Fix more move/rename errors.
601
602
                kind = self.kind(from_rel)
603
0.200.1748 by Jelmer Vernooij
Fix rename test.
604
            if not from_path in self.index and kind != 'directory':
605
                # It's not a file
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
606
                raise errors.BzrMoveFailedError(from_rel, to_rel,
607
                    errors.NotVersionedError(path=from_rel))
0.200.1737 by Jelmer Vernooij
Fix rename tests.
608
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
609
            if not after:
0.200.1737 by Jelmer Vernooij
Fix rename tests.
610
                try:
611
                    os.rename(self.abspath(from_rel), self.abspath(to_rel))
612
                except OSError as e:
613
                    if e.errno == errno.ENOENT:
614
                        raise errors.BzrMoveFailedError(from_rel, to_rel,
615
                            errors.NoSuchFile(to_rel))
616
                    raise
0.200.1748 by Jelmer Vernooij
Fix rename test.
617
            if kind != 'directory':
618
                self.index[to_path] = self.index[from_path]
619
                del self.index[from_path]
0.347.1 by Jelmer Vernooij
Various rename fixes.
620
            else:
621
                todo = [p for p in self.index if p.startswith(from_path+'/')]
622
                for p in todo:
0.347.3 by Jelmer Vernooij
Fix breezy.tests.per_workingtree.test_workingtree.TestWorkingTree.test_rename_dirs.
623
                    self.index[posixpath.join(to_path, posixpath.relpath(p, from_path))] = self.index[p]
0.347.1 by Jelmer Vernooij
Various rename fixes.
624
                    del self.index[p]
625
0.200.1772 by Jelmer Vernooij
Fix handling of unversioned file in get_file_sha1.
626
            self._versioned_dirs = None
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
627
            self.flush()
0.200.1193 by Jelmer Vernooij
Implement GitWorkingTree.{move,rename_one}.
628
0.264.1 by Jelmer Vernooij
Provide stubs using inventory for the moment.:
629
    def get_root_id(self):
0.200.1192 by Jelmer Vernooij
Implement path2id.
630
        return self.path2id("")
631
0.200.1712 by Jelmer Vernooij
Add file_id prefix.
632
    def has_filename(self, filename):
633
        return osutils.lexists(self.abspath(filename))
634
0.200.1242 by Jelmer Vernooij
Support directories better.
635
    def _has_dir(self, path):
0.200.1368 by Jelmer Vernooij
There is always a tree root.
636
        if path == "":
637
            return True
0.200.1242 by Jelmer Vernooij
Support directories better.
638
        if self._versioned_dirs is None:
639
            self._load_dirs()
640
        return path in self._versioned_dirs
641
0.200.1192 by Jelmer Vernooij
Implement path2id.
642
    def path2id(self, path):
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
643
        with self.lock_read():
0.200.1766 by Jelmer Vernooij
Fix another list_files test, integrate is_versioned.
644
            path = path.rstrip('/')
645
            if self.is_versioned(path.rstrip('/')):
646
                return self._fileid_map.lookup_file_id(path.encode("utf-8"))
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
647
            return None
0.264.1 by Jelmer Vernooij
Provide stubs using inventory for the moment.:
648
0.200.1766 by Jelmer Vernooij
Fix another list_files test, integrate is_versioned.
649
    def _iter_files_recursive(self, from_dir=None, include_dirs=False):
0.200.1328 by Jelmer Vernooij
More test fixes.
650
        if from_dir is None:
651
            from_dir = ""
0.341.1 by Jelmer Vernooij
Fix BadFilenameEncoding raising during WorkingTree.extras.
652
        for (dirpath, dirnames, filenames) in os.walk(self.abspath(from_dir).encode(osutils._fs_enc)):
0.200.1302 by Jelmer Vernooij
Significantly improve performance of WorkingTree.extras().
653
            dir_relpath = dirpath[len(self.basedir):].strip("/")
0.200.1648 by Jelmer Vernooij
Fix compatibility with newer versions of breezy.
654
            if self.controldir.is_control_filename(dir_relpath):
0.200.605 by Jelmer Vernooij
Ignore directories in WorkingTree.extras().
655
                continue
0.200.1766 by Jelmer Vernooij
Fix another list_files test, integrate is_versioned.
656
            for name in list(dirnames):
657
                if self.controldir.is_control_filename(name):
658
                    dirnames.remove(name)
659
                    continue
660
                relpath = os.path.join(dir_relpath, name)
661
                if include_dirs:
0.341.1 by Jelmer Vernooij
Fix BadFilenameEncoding raising during WorkingTree.extras.
662
                    try:
663
                        yield relpath.decode(osutils._fs_enc)
664
                    except UnicodeDecodeError as e:
665
                        raise errors.BadFilenameEncoding(
666
                            relpath, osutils._fs_enc)
0.200.1766 by Jelmer Vernooij
Fix another list_files test, integrate is_versioned.
667
                if not self._has_dir(relpath):
668
                    dirnames.remove(name)
669
            for name in filenames:
670
                if not self.mapping.is_special_file(name):
0.341.1 by Jelmer Vernooij
Fix BadFilenameEncoding raising during WorkingTree.extras.
671
                    yp = os.path.join(dir_relpath, name)
672
                    try:
673
                        yield yp.decode(osutils._fs_enc)
674
                    except UnicodeDecodeError:
675
                        raise errors.BadFilenameEncoding(
676
                            yp, osutils._fs_enc)
0.200.1327 by Jelmer Vernooij
Factor out all file browsing in extras.
677
678
    def extras(self):
679
        """Yield all unversioned files in this WorkingTree.
680
        """
0.200.1676 by Jelmer Vernooij
Fix typo.
681
        with self.lock_read():
0.348.1 by Jelmer Vernooij
Support directories in WorkingTree.extras.
682
            for p in (set(self._iter_files_recursive(include_dirs=True)) - set(self.index)):
683
                try:
684
                    up = p.decode(osutils._fs_enc)
685
                except UnicodeDecodeError:
686
                    raise errors.BadFilenameEncoding(
687
                        p, osutils._fs_enc)
688
                if not self._has_dir(up):
689
                    yield up
0.200.605 by Jelmer Vernooij
Ignore directories in WorkingTree.extras().
690
0.200.382 by Jelmer Vernooij
Support flushing index.
691
    def flush(self):
0.287.6 by Jelmer Vernooij
Fix some more tests.
692
        # TODO: Maybe this should only write on dirty ?
693
        if self._lock_mode != 'w':
694
            raise errors.NotWriteLocked(self)
695
        self.index.write()
0.200.382 by Jelmer Vernooij
Support flushing index.
696
0.264.1 by Jelmer Vernooij
Provide stubs using inventory for the moment.:
697
    def __iter__(self):
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
698
        with self.lock_read():
699
            for path in self.index:
700
                yield self.path2id(path)
701
            self._load_dirs()
702
            for path in self._versioned_dirs:
703
                yield self.path2id(path)
0.264.1 by Jelmer Vernooij
Provide stubs using inventory for the moment.:
704
0.200.1202 by Jelmer Vernooij
Implement has_or_had_id.
705
    def has_or_had_id(self, file_id):
706
        if self.has_id(file_id):
707
            return True
708
        if self.had_id(file_id):
709
            return True
710
        return False
711
712
    def had_id(self, file_id):
713
        path = self._basis_fileid_map.lookup_file_id(file_id)
714
        try:
715
            head = self.repository._git.head()
716
        except KeyError:
717
            # Assume no if basis is not accessible
718
            return False
0.336.2 by Jelmer Vernooij
Don't use ZERO_SHA outside of remote communication to indicate empty history.
719
        try:
720
            root_tree = self.store[head].tree
721
        except KeyError:
0.200.1205 by Jelmer Vernooij
Implement GitWorkingTree.stored_kind.
722
            return False
0.200.1202 by Jelmer Vernooij
Implement has_or_had_id.
723
        try:
724
            tree_lookup_path(self.store.__getitem__, root_tree, path)
725
        except KeyError:
726
            return False
727
        else:
728
            return True
729
0.200.1198 by Jelmer Vernooij
Implement GitWorkingTree.has_id.
730
    def has_id(self, file_id):
731
        try:
732
            self.id2path(file_id)
733
        except errors.NoSuchId:
734
            return False
735
        else:
736
            return True
737
0.264.1 by Jelmer Vernooij
Provide stubs using inventory for the moment.:
738
    def id2path(self, file_id):
0.200.1532 by Jelmer Vernooij
Cope with float timestamps.
739
        assert type(file_id) is str, "file id not a string: %r" % file_id
0.200.1411 by Jelmer Vernooij
Fix control files.
740
        file_id = osutils.safe_utf8(file_id)
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
741
        with self.lock_read():
0.200.1712 by Jelmer Vernooij
Add file_id prefix.
742
            try:
743
                path = self._fileid_map.lookup_path(file_id)
744
            except ValueError:
745
                raise errors.NoSuchId(self, file_id)
0.200.1766 by Jelmer Vernooij
Fix another list_files test, integrate is_versioned.
746
            path = path.decode('utf-8')
747
            if self.is_versioned(path):
748
                return path
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
749
            raise errors.NoSuchId(self, file_id)
0.264.1 by Jelmer Vernooij
Provide stubs using inventory for the moment.:
750
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
751
    def get_file_mtime(self, path, file_id=None):
0.200.1200 by Jelmer Vernooij
Support GitWorkingTree.get_file_mtime.
752
        """See Tree.get_file_mtime."""
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
753
        try:
754
            return os.lstat(self.abspath(path)).st_mtime
755
        except OSError, (num, msg):
756
            if num == errno.ENOENT:
757
                raise errors.NoSuchFile(path)
758
            raise
0.200.1200 by Jelmer Vernooij
Support GitWorkingTree.get_file_mtime.
759
0.200.1655 by Jelmer Vernooij
Basic support for git ignores.
760
    def is_ignored(self, filename):
761
        r"""Check whether the filename matches an ignore pattern.
762
763
        If the file is ignored, returns the pattern which caused it to
764
        be ignored, otherwise None.  So this can simply be used as a
765
        boolean if desired."""
766
        if getattr(self, '_global_ignoreglobster', None) is None:
767
            ignore_globs = set()
768
            ignore_globs.update(ignores.get_runtime_ignores())
769
            ignore_globs.update(ignores.get_user_ignores())
770
            self._global_ignoreglobster = globbing.ExceptionGlobster(ignore_globs)
0.200.1656 by Jelmer Vernooij
Report proper patterns, ignore files.
771
        match = self._global_ignoreglobster.match(filename)
772
        if match is not None:
773
            return match
0.200.1716 by Jelmer Vernooij
Fix some more tests.
774
        try:
775
            if self.kind(filename) == 'directory':
776
                filename += b'/'
777
        except errors.NoSuchFile:
778
            pass
779
        filename = filename.lstrip(b'/')
0.200.1658 by Jelmer Vernooij
Fix handling of ignores - return patterns that matched.
780
        ignore_manager = self._get_ignore_manager()
781
        ps = list(ignore_manager.find_matching(filename))
782
        if not ps:
783
            return None
784
        if not ps[-1].is_exclude:
785
            return None
786
        return bytes(ps[-1])
787
788
    def _get_ignore_manager(self):
789
        ignoremanager = getattr(self, '_ignoremanager', None)
790
        if ignoremanager is not None:
791
            return ignoremanager
792
793
        ignore_manager = IgnoreFilterManager.from_repo(self.repository._git)
794
        self._ignoremanager = ignore_manager
795
        return ignore_manager
0.200.409 by Jelmer Vernooij
Support parsing .gitignore.
796
0.200.1716 by Jelmer Vernooij
Fix some more tests.
797
    def _flush_ignore_list_cache(self):
798
        self._ignoremanager = None
799
0.200.508 by Jelmer Vernooij
Skip inventory caching bits.
800
    def set_last_revision(self, revid):
0.200.1720 by Jelmer Vernooij
Fix handling of pending merges.
801
        if _mod_revision.is_null(revid):
802
            self.branch.set_last_revision_info(0, revid)
803
            return False
804
        _mod_revision.check_not_reserved_id(revid)
805
        try:
806
            self.branch.generate_revision_history(revid)
807
        except errors.NoSuchRevision:
808
            raise errors.GhostRevisionUnusableHere(revid)
0.200.508 by Jelmer Vernooij
Skip inventory caching bits.
809
0.200.379 by Jelmer Vernooij
Re-enable working tree support.
810
    def _reset_data(self):
0.248.3 by Jelmer Vernooij
Handle working trees without valid HEAD branch.
811
        try:
812
            head = self.repository._git.head()
0.336.2 by Jelmer Vernooij
Don't use ZERO_SHA outside of remote communication to indicate empty history.
813
        except KeyError:
0.200.1202 by Jelmer Vernooij
Implement has_or_had_id.
814
            self._basis_fileid_map = GitFileIdMap({}, self.mapping)
0.200.948 by Jelmer Vernooij
Cope with empty inventories.
815
        else:
0.336.2 by Jelmer Vernooij
Don't use ZERO_SHA outside of remote communication to indicate empty history.
816
            self._basis_fileid_map = self.mapping.get_fileid_map(
817
                self.store.__getitem__, self.store[head].tree)
0.200.379 by Jelmer Vernooij
Re-enable working tree support.
818
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
819
    def get_file_verifier(self, path, file_id=None, stat_value=None):
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
820
        with self.lock_read():
821
            try:
822
                return ("GIT", self.index[path][-2])
823
            except KeyError:
824
                if self._has_dir(path):
825
                    return ("GIT", None)
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
826
                raise errors.NoSuchFile(path)
0.200.1302 by Jelmer Vernooij
Significantly improve performance of WorkingTree.extras().
827
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
828
    def get_file_sha1(self, path, file_id=None, stat_value=None):
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
829
        with self.lock_read():
0.200.1772 by Jelmer Vernooij
Fix handling of unversioned file in get_file_sha1.
830
            if not self.is_versioned(path):
831
                raise errors.NoSuchFile(path)
832
            abspath = self.abspath(path)
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
833
            try:
834
                return osutils.sha_file_by_name(abspath)
835
            except OSError, (num, msg):
836
                if num in (errno.EISDIR, errno.ENOENT):
0.200.1772 by Jelmer Vernooij
Fix handling of unversioned file in get_file_sha1.
837
                    return None
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
838
                raise
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
839
0.200.610 by Jelmer Vernooij
Support retrieving basis tree properly.
840
    def revision_tree(self, revid):
841
        return self.repository.revision_tree(revid)
842
0.200.1766 by Jelmer Vernooij
Fix another list_files test, integrate is_versioned.
843
    def is_versioned(self, path):
844
        with self.lock_read():
845
            path = path.rstrip('/').encode('utf-8')
846
            return (path in self.index or self._has_dir(path))
0.200.1242 by Jelmer Vernooij
Support directories better.
847
0.200.1239 by Jelmer Vernooij
Implement GitWorkingTree.filter_unversioned_files.
848
    def filter_unversioned_files(self, files):
0.200.1766 by Jelmer Vernooij
Fix another list_files test, integrate is_versioned.
849
        return set([p for p in files if not self.is_versioned(p)])
0.200.1239 by Jelmer Vernooij
Implement GitWorkingTree.filter_unversioned_files.
850
0.264.11 by Jelmer Vernooij
Completer implementation of iter_entries_by_dir and list_files.
851
    def _get_dir_ie(self, path, parent_id):
0.200.1192 by Jelmer Vernooij
Implement path2id.
852
        file_id = self.path2id(path)
0.264.11 by Jelmer Vernooij
Completer implementation of iter_entries_by_dir and list_files.
853
        return inventory.InventoryDirectory(file_id,
0.200.1190 by Jelmer Vernooij
Fix get_symlink_target call.
854
            posixpath.basename(path).strip("/"), parent_id)
0.264.11 by Jelmer Vernooij
Completer implementation of iter_entries_by_dir and list_files.
855
856
    def _add_missing_parent_ids(self, path, dir_ids):
857
        if path in dir_ids:
858
            return []
859
        parent = posixpath.dirname(path).strip("/")
860
        ret = self._add_missing_parent_ids(parent, dir_ids)
861
        parent_id = dir_ids[parent]
862
        ie = self._get_dir_ie(path, parent_id)
863
        dir_ids[path] = ie.file_id
864
        ret.append((path, ie))
865
        return ret
866
0.200.1321 by Jelmer Vernooij
More fixes for compatibility with bzr.dev testsuite.
867
    def _get_file_ie(self, name, path, value, parent_id):
868
        assert isinstance(name, unicode)
0.200.1192 by Jelmer Vernooij
Implement path2id.
869
        assert isinstance(path, unicode)
0.264.10 by Jelmer Vernooij
Yield inventory entries.
870
        assert isinstance(value, tuple) and len(value) == 10
871
        (ctime, mtime, dev, ino, mode, uid, gid, size, sha, flags) = value
0.200.1192 by Jelmer Vernooij
Implement path2id.
872
        file_id = self.path2id(path)
0.264.10 by Jelmer Vernooij
Yield inventory entries.
873
        if type(file_id) != str:
874
            raise AssertionError
875
        kind = mode_kind(mode)
0.200.1321 by Jelmer Vernooij
More fixes for compatibility with bzr.dev testsuite.
876
        ie = inventory.entry_factory[kind](file_id, name, parent_id)
0.264.10 by Jelmer Vernooij
Yield inventory entries.
877
        if kind == 'symlink':
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
878
            ie.symlink_target = self.get_symlink_target(path, file_id)
0.264.10 by Jelmer Vernooij
Yield inventory entries.
879
        else:
0.347.4 by Jelmer Vernooij
Use index for ie data.
880
            try:
881
                data = self.get_file_text(path, file_id)
882
            except errors.NoSuchFile:
883
                data = None
884
            except IOError as e:
885
                if e.errno != errno.ENOENT:
886
                    raise
887
                data = None
888
            if data is None:
889
                data = self.repository._git.object_store[sha].data
0.264.10 by Jelmer Vernooij
Yield inventory entries.
890
            ie.text_sha1 = osutils.sha_string(data)
891
            ie.text_size = len(data)
0.347.4 by Jelmer Vernooij
Use index for ie data.
892
            ie.executable = bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
0.264.10 by Jelmer Vernooij
Yield inventory entries.
893
        ie.revision = None
894
        return ie
895
0.264.11 by Jelmer Vernooij
Completer implementation of iter_entries_by_dir and list_files.
896
    def _is_executable_from_path_and_stat_from_stat(self, path, stat_result):
897
        mode = stat_result.st_mode
898
        return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
899
0.318.1 by Jelmer Vernooij
Fix executable test.
900
    def _is_executable_from_path_and_stat_from_basis(self, path, stat_result):
901
        return self.basis_tree().is_executable(path)
902
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
903
    def stored_kind(self, path, file_id=None):
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
904
        with self.lock_read():
905
            try:
906
                return mode_kind(self.index[path.encode("utf-8")][4])
907
            except KeyError:
908
                # Maybe it's a directory?
909
                if self._has_dir(path):
910
                    return "directory"
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
911
                raise errors.NoSuchFile(path)
0.200.1205 by Jelmer Vernooij
Implement GitWorkingTree.stored_kind.
912
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
913
    def is_executable(self, path, file_id=None):
0.200.1539 by Jelmer Vernooij
Cope with new is_executable.
914
        if getattr(self, "_supports_executable", osutils.supports_executable)():
915
            mode = os.lstat(self.abspath(path)).st_mode
916
        else:
0.319.1 by Jelmer Vernooij
Support falling back on basis for executable bits.
917
            try:
918
                mode = self.index[path.encode('utf-8')].mode
919
            except KeyError:
920
                mode = 0
921
        return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
0.264.11 by Jelmer Vernooij
Completer implementation of iter_entries_by_dir and list_files.
922
0.200.1539 by Jelmer Vernooij
Cope with new is_executable.
923
    def _is_executable_from_path_and_stat(self, path, stat_result):
924
        if getattr(self, "_supports_executable", osutils.supports_executable)():
925
            return self._is_executable_from_path_and_stat_from_stat(path, stat_result)
926
        else:
927
            return self._is_executable_from_path_and_stat_from_basis(path, stat_result)
0.264.11 by Jelmer Vernooij
Completer implementation of iter_entries_by_dir and list_files.
928
0.264.10 by Jelmer Vernooij
Yield inventory entries.
929
    def list_files(self, include_root=False, from_dir=None, recursive=True):
0.200.1321 by Jelmer Vernooij
More fixes for compatibility with bzr.dev testsuite.
930
        if from_dir is None:
931
            from_dir = ""
0.264.11 by Jelmer Vernooij
Completer implementation of iter_entries_by_dir and list_files.
932
        dir_ids = {}
0.200.1767 by Jelmer Vernooij
Fix references to Tree*.
933
        fk_entries = {'directory': tree.TreeDirectory,
934
                      'file': tree.TreeFile,
935
                      'symlink': tree.TreeLink}
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
936
        with self.lock_read():
937
            root_ie = self._get_dir_ie(u"", None)
938
            if include_root and not from_dir:
939
                yield "", "V", root_ie.kind, root_ie.file_id, root_ie
940
            dir_ids[u""] = root_ie.file_id
941
            if recursive:
0.200.1766 by Jelmer Vernooij
Fix another list_files test, integrate is_versioned.
942
                path_iterator = sorted(self._iter_files_recursive(from_dir, include_dirs=True))
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
943
            else:
944
                if from_dir is None:
945
                    start = self.basedir
946
                else:
947
                    start = os.path.join(self.basedir, from_dir)
948
                path_iterator = sorted([os.path.join(from_dir, name) for name in
949
                    os.listdir(start) if not self.controldir.is_control_filename(name)
950
                    and not self.mapping.is_special_file(name)])
951
            for path in path_iterator:
952
                try:
953
                    value = self.index[path]
954
                except KeyError:
955
                    value = None
0.348.1 by Jelmer Vernooij
Support directories in WorkingTree.extras.
956
                try:
957
                    path = path.decode("utf-8")
958
                except UnicodeDecodeError:
959
                    raise errors.BadFilenameEncoding(
960
                        path, osutils._fs_enc)
0.200.1766 by Jelmer Vernooij
Fix another list_files test, integrate is_versioned.
961
                kind = osutils.file_kind(self.abspath(path))
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
962
                parent, name = posixpath.split(path)
963
                for dir_path, dir_ie in self._add_missing_parent_ids(parent, dir_ids):
0.200.1766 by Jelmer Vernooij
Fix another list_files test, integrate is_versioned.
964
                    pass
965
                if kind == 'directory':
966
                    if path != from_dir:
967
                        if self._has_dir(path):
968
                            ie = self._get_dir_ie(path, self.path2id(path))
969
                            status = "V"
970
                            file_id = ie.file_id
971
                        elif self.is_ignored(path):
972
                            status = "I"
973
                            ie = fk_entries[kind]()
974
                            file_id = None
975
                        else:
976
                            status = "?"
977
                            ie = fk_entries[kind]()
978
                            file_id = None
979
                        yield posixpath.relpath(path, from_dir), status, kind, file_id, ie
980
                    continue
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
981
                if value is not None:
982
                    ie = self._get_file_ie(name, path, value, dir_ids[parent])
0.200.1766 by Jelmer Vernooij
Fix another list_files test, integrate is_versioned.
983
                    yield posixpath.relpath(path, from_dir), "V", ie.kind, ie.file_id, ie
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
984
                else:
985
                    ie = fk_entries[kind]()
0.200.1766 by Jelmer Vernooij
Fix another list_files test, integrate is_versioned.
986
                    yield posixpath.relpath(path, from_dir), ("I" if self.is_ignored(path) else "?"), kind, None, ie
0.264.10 by Jelmer Vernooij
Yield inventory entries.
987
0.200.1206 by Jelmer Vernooij
Implement GitWorkingTree.all_file_ids.
988
    def all_file_ids(self):
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
989
        with self.lock_read():
990
            ids = {u"": self.path2id("")}
991
            for path in self.index:
992
                if self.mapping.is_special_file(path):
993
                    continue
994
                path = path.decode("utf-8")
995
                parent = posixpath.dirname(path).strip("/")
996
                for e in self._add_missing_parent_ids(parent, ids):
997
                    pass
998
                ids[path] = self.path2id(path)
999
            return set(ids.values())
0.200.1206 by Jelmer Vernooij
Implement GitWorkingTree.all_file_ids.
1000
0.200.1710 by Jelmer Vernooij
Regenerate xfail.
1001
    def all_versioned_paths(self):
1002
        with self.lock_read():
1003
            paths = {u""}
1004
            for path in self.index:
1005
                if self.mapping.is_special_file(path):
1006
                    continue
1007
                path = path.decode("utf-8")
1008
                paths.add(path)
1009
                while path != "":
1010
                    path = posixpath.dirname(path).strip("/")
1011
                    if path in paths:
1012
                        break
1013
                    paths.add(path)
1014
            return paths
1015
0.200.1374 by Jelmer Vernooij
Implement GitWorkingTree._directory_is_tree_reference.
1016
    def _directory_is_tree_reference(self, path):
1017
        # FIXME: Check .gitsubmodules for path
1018
        return False
1019
0.200.1716 by Jelmer Vernooij
Fix some more tests.
1020
    def iter_child_entries(self, path, file_id=None):
1021
        encoded_path = path.encode('utf-8')
0.200.1739 by Jelmer Vernooij
Fix iteration order for iter_child_entries.
1022
        parent_id = self.path2id(path)
0.200.1738 by Jelmer Vernooij
Fix test_does_not_exist.
1023
        found_any = False
0.200.1739 by Jelmer Vernooij
Fix iteration order for iter_child_entries.
1024
        seen_children = set()
0.200.1716 by Jelmer Vernooij
Fix some more tests.
1025
        for item_path, value in self.index.iteritems():
1026
            if self.mapping.is_special_file(item_path):
1027
                continue
0.200.1739 by Jelmer Vernooij
Fix iteration order for iter_child_entries.
1028
            if not osutils.is_inside(encoded_path, item_path):
0.200.1716 by Jelmer Vernooij
Fix some more tests.
1029
                continue
0.200.1738 by Jelmer Vernooij
Fix test_does_not_exist.
1030
            found_any = True
0.200.1739 by Jelmer Vernooij
Fix iteration order for iter_child_entries.
1031
            subpath = posixpath.relpath(item_path, encoded_path)
0.200.1716 by Jelmer Vernooij
Fix some more tests.
1032
            if b'/' in subpath:
0.200.1739 by Jelmer Vernooij
Fix iteration order for iter_child_entries.
1033
                dirname = subpath.split(b'/', 1)[0]
1034
                file_ie = self._get_dir_ie(posixpath.join(path, dirname), parent_id)
1035
            else:
1036
                (parent, name) = posixpath.split(item_path)
1037
                try:
1038
                    file_ie = self._get_file_ie(name, item_path, value, parent_id)
1039
                except IOError:
1040
                    continue
0.200.1716 by Jelmer Vernooij
Fix some more tests.
1041
            yield file_ie
0.200.1738 by Jelmer Vernooij
Fix test_does_not_exist.
1042
        if not found_any:
1043
            raise errors.NoSuchFile(path)
0.200.1716 by Jelmer Vernooij
Fix some more tests.
1044
0.264.9 by Jelmer Vernooij
Implement basic GitWorkingTree.iter_entries_by_dir.
1045
    def iter_entries_by_dir(self, specific_file_ids=None, yield_parents=False):
0.200.1252 by Jelmer Vernooij
Support specific_file_ids in GitWorkingTree.iter_entries_by_dir.
1046
        if yield_parents:
1047
            raise NotImplementedError(self.iter_entries_by_dir)
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
1048
        with self.lock_read():
1049
            if specific_file_ids is not None:
0.200.1712 by Jelmer Vernooij
Add file_id prefix.
1050
                specific_paths = []
1051
                for file_id in specific_file_ids:
0.315.1 by Jelmer Vernooij
Allow file ids passed to Tree.iter_entries_by_dir to be None.
1052
                    if file_id is None:
1053
                        continue
0.309.1 by Jelmer Vernooij
Allow file-ids to Tree.iter_entries_by_dir to be absent.
1054
                    try:
1055
                        specific_paths.append(self.id2path(file_id))
1056
                    except errors.NoSuchId:
1057
                        pass
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
1058
                if specific_paths in ([u""], []):
1059
                    specific_paths = None
1060
                else:
1061
                    specific_paths = set(specific_paths)
1062
            else:
0.200.1252 by Jelmer Vernooij
Support specific_file_ids in GitWorkingTree.iter_entries_by_dir.
1063
                specific_paths = None
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
1064
            root_ie = self._get_dir_ie(u"", None)
0.200.1740 by Jelmer Vernooij
Fix iter_entries_by_dir order.
1065
            ret = {}
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
1066
            if specific_paths is None:
0.200.1740 by Jelmer Vernooij
Fix iter_entries_by_dir order.
1067
                ret[(None, u"")] = root_ie
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
1068
            dir_ids = {u"": root_ie.file_id}
1069
            for path, value in self.index.iteritems():
1070
                if self.mapping.is_special_file(path):
1071
                    continue
1072
                path = path.decode("utf-8")
1073
                if specific_paths is not None and not path in specific_paths:
1074
                    continue
1075
                (parent, name) = posixpath.split(path)
1076
                try:
1077
                    file_ie = self._get_file_ie(name, path, value, None)
0.319.1 by Jelmer Vernooij
Support falling back on basis for executable bits.
1078
                except errors.NoSuchFile:
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
1079
                    continue
0.200.1730 by Jelmer Vernooij
Fix test_is_executable_dir.
1080
                if yield_parents or specific_file_ids is None:
1081
                    for (dir_path, dir_ie) in self._add_missing_parent_ids(parent,
1082
                            dir_ids):
0.200.1740 by Jelmer Vernooij
Fix iter_entries_by_dir order.
1083
                        ret[(posixpath.dirname(dir_path), dir_path)] = dir_ie
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
1084
                file_ie.parent_id = self.path2id(parent)
0.200.1740 by Jelmer Vernooij
Fix iter_entries_by_dir order.
1085
                ret[(posixpath.dirname(path), path)] = file_ie
1086
            return ((path, ie) for ((_, path), ie) in sorted(ret.items()))
0.264.9 by Jelmer Vernooij
Implement basic GitWorkingTree.iter_entries_by_dir.
1087
0.200.619 by Jelmer Vernooij
Provide dummy WorkingTree.conflicts() implementation rather than spending a lot of time not finding any conflicts.
1088
    def conflicts(self):
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
1089
        with self.lock_read():
1090
            # FIXME:
1091
            return _mod_conflicts.ConflictList()
0.200.619 by Jelmer Vernooij
Provide dummy WorkingTree.conflicts() implementation rather than spending a lot of time not finding any conflicts.
1092
0.200.1705 by Jelmer Vernooij
Fix walkdirs.
1093
    def walkdirs(self, prefix=""):
0.324.1 by Jelmer Vernooij
initial work fixing walkdirs.
1094
        """Walk the directories of this tree.
1095
1096
        returns a generator which yields items in the form:
1097
                ((curren_directory_path, fileid),
1098
                 [(file1_path, file1_name, file1_kind, (lstat), file1_id,
1099
                   file1_kind), ... ])
1100
1101
        This API returns a generator, which is only valid during the current
1102
        tree transaction - within a single lock_read or lock_write duration.
1103
1104
        If the tree is not locked, it may cause an error to be raised,
1105
        depending on the tree implementation.
1106
        """
1107
        from bisect import bisect_left
1108
        import operator
1109
        disk_top = self.abspath(prefix)
1110
        if disk_top.endswith('/'):
1111
            disk_top = disk_top[:-1]
1112
        top_strip_len = len(disk_top) + 1
1113
        inventory_iterator = self._walkdirs(prefix)
1114
        disk_iterator = osutils.walkdirs(disk_top, prefix)
1115
        try:
1116
            current_disk = next(disk_iterator)
1117
            disk_finished = False
1118
        except OSError as e:
1119
            if not (e.errno == errno.ENOENT or
1120
                (sys.platform == 'win32' and e.errno == ERROR_PATH_NOT_FOUND)):
1121
                raise
1122
            current_disk = None
1123
            disk_finished = True
1124
        try:
1125
            current_inv = next(inventory_iterator)
1126
            inv_finished = False
1127
        except StopIteration:
1128
            current_inv = None
1129
            inv_finished = True
1130
        while not inv_finished or not disk_finished:
1131
            if current_disk:
1132
                ((cur_disk_dir_relpath, cur_disk_dir_path_from_top),
1133
                    cur_disk_dir_content) = current_disk
1134
            else:
1135
                ((cur_disk_dir_relpath, cur_disk_dir_path_from_top),
1136
                    cur_disk_dir_content) = ((None, None), None)
1137
            if not disk_finished:
1138
                # strip out .bzr dirs
1139
                if (cur_disk_dir_path_from_top[top_strip_len:] == '' and
1140
                    len(cur_disk_dir_content) > 0):
1141
                    # osutils.walkdirs can be made nicer -
1142
                    # yield the path-from-prefix rather than the pathjoined
1143
                    # value.
1144
                    bzrdir_loc = bisect_left(cur_disk_dir_content,
1145
                        ('.git', '.git'))
1146
                    if (bzrdir_loc < len(cur_disk_dir_content)
1147
                        and self.controldir.is_control_filename(
1148
                            cur_disk_dir_content[bzrdir_loc][0])):
1149
                        # we dont yield the contents of, or, .bzr itself.
1150
                        del cur_disk_dir_content[bzrdir_loc]
1151
            if inv_finished:
1152
                # everything is unknown
1153
                direction = 1
1154
            elif disk_finished:
1155
                # everything is missing
1156
                direction = -1
1157
            else:
1158
                direction = cmp(current_inv[0][0], cur_disk_dir_relpath)
1159
            if direction > 0:
1160
                # disk is before inventory - unknown
1161
                dirblock = [(relpath, basename, kind, stat, None, None) for
1162
                    relpath, basename, kind, stat, top_path in
1163
                    cur_disk_dir_content]
1164
                yield (cur_disk_dir_relpath, None), dirblock
1165
                try:
1166
                    current_disk = next(disk_iterator)
1167
                except StopIteration:
1168
                    disk_finished = True
1169
            elif direction < 0:
1170
                # inventory is before disk - missing.
1171
                dirblock = [(relpath, basename, 'unknown', None, fileid, kind)
1172
                    for relpath, basename, dkind, stat, fileid, kind in
1173
                    current_inv[1]]
1174
                yield (current_inv[0][0], current_inv[0][1]), dirblock
1175
                try:
1176
                    current_inv = next(inventory_iterator)
1177
                except StopIteration:
1178
                    inv_finished = True
1179
            else:
1180
                # versioned present directory
1181
                # merge the inventory and disk data together
1182
                dirblock = []
1183
                for relpath, subiterator in itertools.groupby(sorted(
1184
                    current_inv[1] + cur_disk_dir_content,
1185
                    key=operator.itemgetter(0)), operator.itemgetter(1)):
1186
                    path_elements = list(subiterator)
1187
                    if len(path_elements) == 2:
1188
                        inv_row, disk_row = path_elements
1189
                        # versioned, present file
1190
                        dirblock.append((inv_row[0],
1191
                            inv_row[1], disk_row[2],
1192
                            disk_row[3], inv_row[4],
1193
                            inv_row[5]))
1194
                    elif len(path_elements[0]) == 5:
1195
                        # unknown disk file
1196
                        dirblock.append((path_elements[0][0],
1197
                            path_elements[0][1], path_elements[0][2],
1198
                            path_elements[0][3], None, None))
1199
                    elif len(path_elements[0]) == 6:
1200
                        # versioned, absent file.
1201
                        dirblock.append((path_elements[0][0],
1202
                            path_elements[0][1], 'unknown', None,
1203
                            path_elements[0][4], path_elements[0][5]))
1204
                    else:
1205
                        raise NotImplementedError('unreachable code')
1206
                yield current_inv[0], dirblock
1207
                try:
1208
                    current_inv = next(inventory_iterator)
1209
                except StopIteration:
1210
                    inv_finished = True
1211
                try:
1212
                    current_disk = next(disk_iterator)
1213
                except StopIteration:
1214
                    disk_finished = True
1215
1216
    def _walkdirs(self, prefix=""):
0.200.1210 by Jelmer Vernooij
Implement GitWorkingTree._walkdirs.
1217
        if prefix != "":
1218
            prefix += "/"
1219
        per_dir = defaultdict(list)
0.325.2 by Jelmer Vernooij
Handle root in walkdirs.
1220
        if prefix == "":
1221
            per_dir[('', self.get_root_id())] = []
0.200.1210 by Jelmer Vernooij
Implement GitWorkingTree._walkdirs.
1222
        for path, value in self.index.iteritems():
0.200.1328 by Jelmer Vernooij
More test fixes.
1223
            if self.mapping.is_special_file(path):
1224
                continue
0.200.1210 by Jelmer Vernooij
Implement GitWorkingTree._walkdirs.
1225
            if not path.startswith(prefix):
1226
                continue
1227
            (dirname, child_name) = posixpath.split(path)
1228
            dirname = dirname.decode("utf-8")
1229
            dir_file_id = self.path2id(dirname)
1230
            assert isinstance(value, tuple) and len(value) == 10
1231
            per_dir[(dirname, dir_file_id)].append(
1232
                (path.decode("utf-8"), child_name.decode("utf-8"),
0.324.1 by Jelmer Vernooij
initial work fixing walkdirs.
1233
                mode_kind(value.mode), None,
0.200.1210 by Jelmer Vernooij
Implement GitWorkingTree._walkdirs.
1234
                self.path2id(path.decode("utf-8")),
0.293.1 by Jelmer Vernooij
Fix walkdirs handling of unknown entries, mode.
1235
                mode_kind(value.mode)))
0.324.1 by Jelmer Vernooij
initial work fixing walkdirs.
1236
        return ((k, sorted(v)) for (k, v) in per_dir.iteritems())
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
1237
0.200.1542 by Jelmer Vernooij
Refactor iter_changes.
1238
    def _lookup_entry(self, path, update_index=False):
0.200.1543 by Jelmer Vernooij
Support symlinks.
1239
        assert type(path) == str
0.200.1542 by Jelmer Vernooij
Refactor iter_changes.
1240
        entry = self.index[path]
1241
        index_mode = entry[-6]
1242
        index_sha = entry[-2]
0.200.1741 by Jelmer Vernooij
Fix opentree tests.
1243
        disk_path = self.abspath(path.decode('utf-8')).encode(
1244
            osutils._fs_enc)
0.284.1 by Jelmer Vernooij
Raise KeyError when file was removed.
1245
        try:
1246
            disk_stat = os.lstat(disk_path)
1247
        except OSError, (num, msg):
1248
            if num in (errno.EISDIR, errno.ENOENT):
1249
                raise KeyError(path)
1250
            raise
0.200.1542 by Jelmer Vernooij
Refactor iter_changes.
1251
        disk_mtime = disk_stat.st_mtime
1252
        if isinstance(entry[1], tuple):
1253
            index_mtime = entry[1][0]
1254
        else:
1255
            index_mtime = int(entry[1])
1256
        mtime_delta = (disk_mtime - index_mtime)
1257
        disk_mode = cleanup_mode(disk_stat.st_mode)
1258
        if mtime_delta > 0 or disk_mode != index_mode:
1259
            if stat.S_ISDIR(disk_mode):
1260
                try:
1261
                    subrepo = Repo(disk_path)
1262
                except NotGitRepository:
1263
                    return (None, None)
1264
                else:
1265
                    disk_mode = S_IFGITLINK
1266
                    git_id = subrepo.head()
0.200.1543 by Jelmer Vernooij
Support symlinks.
1267
            elif stat.S_ISLNK(disk_mode):
0.200.1715 by Jelmer Vernooij
Fix some more tests.
1268
                blob = Blob.from_string(os.readlink(disk_path))
0.200.1543 by Jelmer Vernooij
Support symlinks.
1269
                git_id = blob.id
1270
            elif stat.S_ISREG(disk_mode):
0.200.1542 by Jelmer Vernooij
Refactor iter_changes.
1271
                with open(disk_path, 'r') as f:
1272
                    blob = Blob.from_string(f.read())
1273
                git_id = blob.id
0.200.1543 by Jelmer Vernooij
Support symlinks.
1274
            else:
1275
                raise AssertionError
0.200.1542 by Jelmer Vernooij
Refactor iter_changes.
1276
            if update_index:
1277
                flags = 0 # FIXME
0.200.1545 by Jelmer Vernooij
Some more test fixes.
1278
                self.index[path] = index_entry_from_stat(disk_stat, git_id, flags, disk_mode)
0.200.1542 by Jelmer Vernooij
Refactor iter_changes.
1279
            return (git_id, disk_mode)
1280
        return (index_sha, index_mode)
1281
0.200.1677 by Jelmer Vernooij
Mark shelving as unsupported.
1282
    def get_shelf_manager(self):
0.200.1729 by Jelmer Vernooij
ShelvingUnsupported doesn't take an argument.
1283
        raise workingtree.ShelvingUnsupported()
0.200.1677 by Jelmer Vernooij
Mark shelving as unsupported.
1284
0.200.1678 by Jelmer Vernooij
Fix tests.
1285
    def store_uncommitted(self):
1286
        raise errors.StoringUncommittedNotSupported(self)
1287
0.200.1703 by Jelmer Vernooij
Implement apply_inventory_delta.
1288
    def apply_inventory_delta(self, changes):
1289
        for (old_path, new_path, file_id, ie) in changes:
1290
            if old_path is not None:
1291
                del self.index[old_path.encode('utf-8')]
0.200.1772 by Jelmer Vernooij
Fix handling of unversioned file in get_file_sha1.
1292
                self._versioned_dirs = None
0.200.1703 by Jelmer Vernooij
Implement apply_inventory_delta.
1293
            if new_path is not None and ie.kind != 'directory':
1294
                self._index_add_entry(new_path, ie.kind)
0.316.2 by Jelmer Vernooij
Flush after modifying index.
1295
        self.flush()
0.200.1703 by Jelmer Vernooij
Implement apply_inventory_delta.
1296
0.200.1756 by Jelmer Vernooij
Initial work on annotate support.
1297
    def annotate_iter(self, path, file_id=None,
1298
                      default_revision=_mod_revision.CURRENT_REVISION):
1299
        """See Tree.annotate_iter
1300
1301
        This implementation will use the basis tree implementation if possible.
1302
        Lines not in the basis are attributed to CURRENT_REVISION
1303
1304
        If there are pending merges, lines added by those merges will be
1305
        incorrectly attributed to CURRENT_REVISION (but after committing, the
1306
        attribution will be correct).
1307
        """
1308
        with self.lock_read():
1309
            maybe_file_parent_keys = []
1310
            for parent_id in self.get_parent_ids():
1311
                try:
1312
                    parent_tree = self.revision_tree(parent_id)
1313
                except errors.NoSuchRevisionInTree:
1314
                    parent_tree = self.branch.repository.revision_tree(
1315
                            parent_id)
1316
                with parent_tree.lock_read():
1317
                    # TODO(jelmer): Use rename/copy tracker to find path name in parent
1318
                    parent_path = path
1319
                    try:
1320
                        kind = parent_tree.kind(parent_path)
1321
                    except errors.NoSuchFile:
1322
                        continue
1323
                    if kind != 'file':
1324
                        # Note: this is slightly unnecessary, because symlinks and
1325
                        # directories have a "text" which is the empty text, and we
1326
                        # know that won't mess up annotations. But it seems cleaner
1327
                        continue
1328
                    parent_text_key = (
1329
                        parent_path,
1330
                        parent_tree.get_file_revision(parent_path))
1331
                    if parent_text_key not in maybe_file_parent_keys:
1332
                        maybe_file_parent_keys.append(parent_text_key)
1333
            graph = self.branch.repository.get_file_graph()
1334
            heads = graph.heads(maybe_file_parent_keys)
1335
            file_parent_keys = []
1336
            for key in maybe_file_parent_keys:
1337
                if key in heads:
1338
                    file_parent_keys.append(key)
1339
1340
            # Now we have the parents of this content
1341
            from breezy.annotate import Annotator
1342
            from .annotate import AnnotateProvider
1343
            annotator = Annotator(AnnotateProvider(
1344
                self.branch.repository._file_change_scanner))
1345
            text = self.get_file_text(path)
1346
            this_key = (path, default_revision)
1347
            annotator.add_special_text(this_key, file_parent_keys, text)
1348
            annotations = [(key[-1], line)
1349
                           for key, line in annotator.annotate_flat(this_key)]
1350
            return annotations
1351
0.200.1308 by Jelmer Vernooij
Write index to disk after adding files.
1352
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
1353
class GitWorkingTreeFormat(workingtree.WorkingTreeFormat):
1354
0.200.1206 by Jelmer Vernooij
Implement GitWorkingTree.all_file_ids.
1355
    _tree_class = GitWorkingTree
1356
0.200.1295 by Jelmer Vernooij
Mark working trees as not supporting directories.
1357
    supports_versioned_directories = False
1358
0.200.1661 by Jelmer Vernooij
Set supports_setting_file_ids to False.
1359
    supports_setting_file_ids = False
1360
0.200.1677 by Jelmer Vernooij
Mark shelving as unsupported.
1361
    supports_store_uncommitted = False
1362
0.200.1723 by Jelmer Vernooij
Set supports_leftmost_parent_id_as_ghost property.
1363
    supports_leftmost_parent_id_as_ghost = False
1364
0.200.1779 by Jelmer Vernooij
Actually store git revision ids rather than bzr revision ids.
1365
    supports_righthand_parent_id_as_ghost = False
1366
0.200.1768 by Jelmer Vernooij
Fix handling of normalized filenames.
1367
    requires_normalized_unicode_filenames = True
1368
0.200.656 by Jelmer Vernooij
Implement GitWorkingTreeFormat._matchingbzrdir.
1369
    @property
0.200.1665 by Jelmer Vernooij
Rename _matchingbzrdir to _matchingcnotroldir.
1370
    def _matchingcontroldir(self):
0.200.1641 by Jelmer Vernooij
Use relative imports where possible.
1371
        from .dir import LocalGitControlDirFormat
0.200.1013 by Jelmer Vernooij
More renames.
1372
        return LocalGitControlDirFormat()
0.200.656 by Jelmer Vernooij
Implement GitWorkingTreeFormat._matchingbzrdir.
1373
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
1374
    def get_format_description(self):
1375
        return "Git Working Tree"
0.200.616 by Jelmer Vernooij
Provide custom intertree implementation for GitRevisionTree->GitWorkingTree.
1376
0.200.1648 by Jelmer Vernooij
Fix compatibility with newer versions of breezy.
1377
    def initialize(self, a_controldir, revision_id=None, from_branch=None,
0.200.1096 by Jelmer Vernooij
Implement GitWorkingTreeFormat.initialize.
1378
                   accelerator_tree=None, hardlink=False):
1379
        """See WorkingTreeFormat.initialize()."""
0.200.1648 by Jelmer Vernooij
Fix compatibility with newer versions of breezy.
1380
        if not isinstance(a_controldir, LocalGitDir):
1381
            raise errors.IncompatibleFormat(self, a_controldir)
1382
        index = Index(a_controldir.root_transport.local_abspath(".git/index"))
0.200.1096 by Jelmer Vernooij
Implement GitWorkingTreeFormat.initialize.
1383
        index.write()
0.308.1 by Jelmer Vernooij
Set revision id if specified to WorkingTreeFormat.initialize.
1384
        branch = a_controldir.open_branch()
1385
        if revision_id is not None:
1386
            branch.set_last_revision(revision_id)
0.200.1680 by Jelmer Vernooij
Fix repo locks.
1387
        wt = GitWorkingTree(
0.308.1 by Jelmer Vernooij
Set revision id if specified to WorkingTreeFormat.initialize.
1388
                a_controldir, a_controldir.open_repository(), branch, index)
0.200.1680 by Jelmer Vernooij
Fix repo locks.
1389
        for hook in MutableTree.hooks['post_build_tree']:
1390
            hook(wt)
1391
        return wt
0.200.1096 by Jelmer Vernooij
Implement GitWorkingTreeFormat.initialize.
1392
0.200.616 by Jelmer Vernooij
Provide custom intertree implementation for GitRevisionTree->GitWorkingTree.
1393
0.287.3 by Jelmer Vernooij
Some improvements to changes iterator.
1394
class InterIndexGitTree(InterGitTrees):
0.200.616 by Jelmer Vernooij
Provide custom intertree implementation for GitRevisionTree->GitWorkingTree.
1395
    """InterTree that works between a Git revision tree and an index."""
1396
1397
    def __init__(self, source, target):
1398
        super(InterIndexGitTree, self).__init__(source, target)
1399
        self._index = target.index
1400
1401
    @classmethod
1402
    def is_compatible(cls, source, target):
0.200.1641 by Jelmer Vernooij
Use relative imports where possible.
1403
        from .repository import GitRevisionTree
0.200.1636 by Jelmer Vernooij
Some formatting fixes.
1404
        return (isinstance(source, GitRevisionTree) and
0.200.616 by Jelmer Vernooij
Provide custom intertree implementation for GitRevisionTree->GitWorkingTree.
1405
                isinstance(target, GitWorkingTree))
1406
0.287.3 by Jelmer Vernooij
Some improvements to changes iterator.
1407
    def _iter_git_changes(self, want_unchanged=False, specific_files=None,
1408
            require_versioned=False, include_root=False):
1409
        # TODO(jelmer): Handle include_root
1410
        # TODO(jelmer): Handle require_versioned
1411
        # TODO(jelmer): Restrict to specific_files, for performance reasons.
1412
        with self.lock_read():
0.200.1754 by Jelmer Vernooij
Avoid updating index when just comparing trees.
1413
            return changes_between_git_tree_and_working_copy(
0.287.3 by Jelmer Vernooij
Some improvements to changes iterator.
1414
                self.source.store, self.source.tree,
1415
                self.target, want_unchanged=want_unchanged)
1416
0.200.616 by Jelmer Vernooij
Provide custom intertree implementation for GitRevisionTree->GitWorkingTree.
1417
    def compare(self, want_unchanged=False, specific_files=None,
1418
                extra_trees=None, require_versioned=False, include_root=False,
1419
                want_unversioned=False):
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
1420
        with self.lock_read():
0.287.3 by Jelmer Vernooij
Some improvements to changes iterator.
1421
            changes = self._iter_git_changes(
1422
                    want_unchanged=want_unchanged,
1423
                    specific_files=specific_files,
1424
                    require_versioned=require_versioned,
1425
                    include_root=include_root)
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
1426
            source_fileid_map = self.source._fileid_map
1427
            target_fileid_map = self.target._fileid_map
1428
            ret = tree_delta_from_git_changes(changes, self.target.mapping,
1429
                (source_fileid_map, target_fileid_map),
0.287.6 by Jelmer Vernooij
Fix some more tests.
1430
                specific_files=specific_files, require_versioned=require_versioned,
1431
                include_root=include_root)
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
1432
            if want_unversioned:
1433
                for e in self.target.extras():
0.200.1731 by Jelmer Vernooij
Add support for checking untracked changes.
1434
                    ret.unversioned.append(
0.200.1732 by Jelmer Vernooij
Fix ignore.
1435
                        (osutils.normalized_filename(e)[0], None,
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
1436
                        osutils.file_kind(self.target.abspath(e))))
1437
            return ret
0.200.616 by Jelmer Vernooij
Provide custom intertree implementation for GitRevisionTree->GitWorkingTree.
1438
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
1439
    def iter_changes(self, include_unchanged=False, specific_files=None,
0.287.3 by Jelmer Vernooij
Some improvements to changes iterator.
1440
                     pb=None, extra_trees=[], require_versioned=True,
1441
                     want_unversioned=False):
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
1442
        with self.lock_read():
0.287.3 by Jelmer Vernooij
Some improvements to changes iterator.
1443
            changes = self._iter_git_changes(
1444
                    want_unchanged=include_unchanged,
1445
                    specific_files=specific_files,
1446
                    require_versioned=require_versioned)
0.200.1731 by Jelmer Vernooij
Add support for checking untracked changes.
1447
            if want_unversioned:
1448
                changes = itertools.chain(
1449
                        changes,
1450
                        untracked_changes(self.target))
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
1451
            return changes_from_git_changes(
0.287.3 by Jelmer Vernooij
Some improvements to changes iterator.
1452
                    changes, self.target.mapping,
1453
                    specific_files=specific_files)
0.200.616 by Jelmer Vernooij
Provide custom intertree implementation for GitRevisionTree->GitWorkingTree.
1454
0.200.1179 by Jelmer Vernooij
Avoid using verifiers for natively imported revisions, save a lot of time.
1455
0.200.616 by Jelmer Vernooij
Provide custom intertree implementation for GitRevisionTree->GitWorkingTree.
1456
tree.InterTree.register_optimiser(InterIndexGitTree)
0.200.1529 by Jelmer Vernooij
Add changes_between_tree_and_index.
1457
1458
0.200.1731 by Jelmer Vernooij
Add support for checking untracked changes.
1459
def untracked_changes(tree):
1460
    for e in tree.extras():
1461
        ap = tree.abspath(e)
1462
        st = os.stat(ap)
1463
        try:
1464
            np, accessible  = osutils.normalized_filename(e)
1465
        except UnicodeDecodeError:
1466
            raise errors.BadFilenameEncoding(
1467
                e, osutils._fs_enc)
0.323.1 by Jelmer Vernooij
Fix iter_changes of untracked changes.
1468
        yield ((None, np), (None, st.st_mode),
1469
               (None, blob_from_path_and_stat(ap.encode('utf-8'), st).id))
0.200.1731 by Jelmer Vernooij
Add support for checking untracked changes.
1470
1471
0.287.3 by Jelmer Vernooij
Some improvements to changes iterator.
1472
def changes_between_git_tree_and_index(store, from_tree_sha, target,
0.200.1731 by Jelmer Vernooij
Add support for checking untracked changes.
1473
        want_unchanged=False, update_index=False):
0.200.1529 by Jelmer Vernooij
Add changes_between_tree_and_index.
1474
    """Determine the changes between a git tree and a working tree with index.
1475
1476
    """
0.287.3 by Jelmer Vernooij
Some improvements to changes iterator.
1477
    to_tree_sha = target.index.commit(store)
0.200.1754 by Jelmer Vernooij
Avoid updating index when just comparing trees.
1478
    return store.tree_changes(from_tree_sha, to_tree_sha, include_trees=True,
0.339.2 by Jelmer Vernooij
Use change_type_same in workingtree.
1479
            want_unchanged=want_unchanged, change_type_same=True)
0.200.1754 by Jelmer Vernooij
Avoid updating index when just comparing trees.
1480
1481
1482
def changes_between_git_tree_and_working_copy(store, from_tree_sha, target,
1483
        want_unchanged=False, update_index=False):
1484
    """Determine the changes between a git tree and a working tree with index.
1485
1486
    """
1487
    blobs = iter_fresh_blobs(target.index, target.abspath('.').encode(sys.getfilesystemencoding()))
1488
    to_tree_sha = commit_tree(store, blobs)
0.287.3 by Jelmer Vernooij
Some improvements to changes iterator.
1489
    return store.tree_changes(from_tree_sha, to_tree_sha, include_trees=True,
0.339.2 by Jelmer Vernooij
Use change_type_same in workingtree.
1490
            want_unchanged=want_unchanged, change_type_same=True)