/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.385 by Jelmer Vernooij
Cope with removed files.
22
from cStringIO import (
23
    StringIO,
24
    )
0.200.1210 by Jelmer Vernooij
Implement GitWorkingTree._walkdirs.
25
from collections import defaultdict
0.239.4 by Jelmer Vernooij
Cope with nonexistent files and directories in get_file_sha1.
26
import errno
0.200.1538 by Jelmer Vernooij
More work on tree-reference support.
27
from dulwich.errors import NotGitRepository
0.200.1655 by Jelmer Vernooij
Basic support for git ignores.
28
from dulwich.ignore import (
0.200.1658 by Jelmer Vernooij
Fix handling of ignores - return patterns that matched.
29
    IgnoreFilterManager,
0.200.1655 by Jelmer Vernooij
Basic support for git ignores.
30
    )
0.200.1096 by Jelmer Vernooij
Implement GitWorkingTreeFormat.initialize.
31
from dulwich.index import (
32
    Index,
0.200.1531 by Jelmer Vernooij
Don't trust index contents - verify against file timestamps.
33
    changes_from_tree,
34
    cleanup_mode,
35
    index_entry_from_stat,
0.200.1096 by Jelmer Vernooij
Implement GitWorkingTreeFormat.initialize.
36
    )
0.200.1202 by Jelmer Vernooij
Implement has_or_had_id.
37
from dulwich.object_store import (
38
    tree_lookup_path,
39
    )
0.200.383 by Jelmer Vernooij
Simplify, support rewriting index based on inventory.
40
from dulwich.objects import (
41
    Blob,
0.200.1538 by Jelmer Vernooij
More work on tree-reference support.
42
    S_IFGITLINK,
0.200.948 by Jelmer Vernooij
Cope with empty inventories.
43
    ZERO_SHA,
44
    )
0.200.1538 by Jelmer Vernooij
More work on tree-reference support.
45
from dulwich.repo import Repo
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
46
import os
0.264.10 by Jelmer Vernooij
Yield inventory entries.
47
import posixpath
0.200.1655 by Jelmer Vernooij
Basic support for git ignores.
48
import re
0.200.384 by Jelmer Vernooij
Fix reading of inventory from index.
49
import stat
0.200.1215 by Jelmer Vernooij
Implement GitWorkingTree.remove.
50
import sys
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
51
0.200.1641 by Jelmer Vernooij
Use relative imports where possible.
52
from ... import (
0.200.382 by Jelmer Vernooij
Support flushing index.
53
    errors,
0.262.1 by Jelmer Vernooij
Fix WorkingTree.conflicts().
54
    conflicts as _mod_conflicts,
0.200.1655 by Jelmer Vernooij
Basic support for git ignores.
55
    globbing,
0.200.409 by Jelmer Vernooij
Support parsing .gitignore.
56
    ignores,
0.200.1476 by Jelmer Vernooij
Cope with working tree refactoring.
57
    lock,
0.200.381 by Jelmer Vernooij
Support working trees properly, status and ls.
58
    osutils,
0.200.1215 by Jelmer Vernooij
Implement GitWorkingTree.remove.
59
    trace,
0.200.519 by Jelmer Vernooij
Move imports down, might not be available in older bzr-git versions.
60
    tree,
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
61
    workingtree,
62
    )
0.200.1648 by Jelmer Vernooij
Fix compatibility with newer versions of breezy.
63
from ...bzr import (
64
    inventory,
65
    )
0.200.1680 by Jelmer Vernooij
Fix repo locks.
66
from ...mutabletree import (
67
    MutableTree,
68
    )
0.200.1641 by Jelmer Vernooij
Use relative imports where possible.
69
70
71
from .dir import (
0.200.1096 by Jelmer Vernooij
Implement GitWorkingTreeFormat.initialize.
72
    LocalGitDir,
73
    )
0.200.1641 by Jelmer Vernooij
Use relative imports where possible.
74
from .tree import (
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
75
    changes_from_git_changes,
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
76
    tree_delta_from_git_changes,
77
    )
0.200.1641 by Jelmer Vernooij
Use relative imports where possible.
78
from .mapping import (
0.200.971 by Chadrik
Fix 'bzr status' after 'bzr add' in native git working trees.
79
    GitFileIdMap,
0.264.10 by Jelmer Vernooij
Yield inventory entries.
80
    mode_kind,
0.200.971 by Chadrik
Fix 'bzr status' after 'bzr add' in native git working trees.
81
    )
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
82
0.200.409 by Jelmer Vernooij
Support parsing .gitignore.
83
IGNORE_FILENAME = ".gitignore"
84
85
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
86
class GitWorkingTree(workingtree.WorkingTree):
87
    """A Git working tree."""
88
0.200.1648 by Jelmer Vernooij
Fix compatibility with newer versions of breezy.
89
    def __init__(self, controldir, repo, branch, index):
90
        self.basedir = controldir.root_transport.local_abspath('.').encode(osutils._fs_enc)
91
        self.controldir = controldir
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
92
        self.repository = repo
0.200.1205 by Jelmer Vernooij
Implement GitWorkingTree.stored_kind.
93
        self.store = self.repository._git.object_store
0.200.384 by Jelmer Vernooij
Fix reading of inventory from index.
94
        self.mapping = self.repository.get_mapping()
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
95
        self._branch = branch
0.200.1648 by Jelmer Vernooij
Fix compatibility with newer versions of breezy.
96
        self._transport = controldir.transport
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
97
        self._format = GitWorkingTreeFormat()
0.200.803 by Jelmer Vernooij
Default to non-bare repositories when initializing a control directory.
98
        self.index = index
0.200.1242 by Jelmer Vernooij
Support directories better.
99
        self._versioned_dirs = None
0.200.239 by Jelmer Vernooij
Provide views.
100
        self.views = self._make_views()
0.200.1173 by Jelmer Vernooij
Provide GitWorkingTree._rules_searcher.
101
        self._rules_searcher = None
0.200.381 by Jelmer Vernooij
Support working trees properly, status and ls.
102
        self._detect_case_handling()
0.200.1202 by Jelmer Vernooij
Implement has_or_had_id.
103
        self._reset_data()
104
        self._fileid_map = self._basis_fileid_map.copy()
0.200.1476 by Jelmer Vernooij
Cope with working tree refactoring.
105
        self._lock_mode = None
106
        self._lock_count = 0
107
0.200.1650 by Jelmer Vernooij
Implement GitWorkingTree.supports_tree_reference.
108
    def supports_tree_reference(self):
109
        return False
110
0.200.1476 by Jelmer Vernooij
Cope with working tree refactoring.
111
    def lock_read(self):
112
        """Lock the repository for read operations.
113
0.200.1646 by Jelmer Vernooij
Rename bzrlib to breezy.
114
        :return: A breezy.lock.LogicalLockResult.
0.200.1476 by Jelmer Vernooij
Cope with working tree refactoring.
115
        """
116
        if not self._lock_mode:
117
            self._lock_mode = 'r'
118
            self._lock_count = 1
0.200.1525 by Jelmer Vernooij
Make sure to always use an up-to-date index.
119
            self.index.read()
0.200.1476 by Jelmer Vernooij
Cope with working tree refactoring.
120
        else:
121
            self._lock_count += 1
122
        self.branch.lock_read()
123
        return lock.LogicalLockResult(self.unlock)
124
0.200.1477 by Jelmer Vernooij
Implement GitWorkingTree.lock_tree_write.
125
    def lock_tree_write(self):
126
        if not self._lock_mode:
127
            self._lock_mode = 'w'
128
            self._lock_count = 1
0.200.1525 by Jelmer Vernooij
Make sure to always use an up-to-date index.
129
            self.index.read()
0.200.1477 by Jelmer Vernooij
Implement GitWorkingTree.lock_tree_write.
130
        elif self._lock_mode == 'r':
131
            raise errors.ReadOnlyError(self)
132
        else:
133
            self._lock_count +=1
134
        self.branch.lock_read()
135
        return lock.LogicalLockResult(self.unlock)
136
0.200.1476 by Jelmer Vernooij
Cope with working tree refactoring.
137
    def lock_write(self, token=None):
138
        if not self._lock_mode:
139
            self._lock_mode = 'w'
140
            self._lock_count = 1
0.200.1525 by Jelmer Vernooij
Make sure to always use an up-to-date index.
141
            self.index.read()
0.200.1476 by Jelmer Vernooij
Cope with working tree refactoring.
142
        elif self._lock_mode == 'r':
143
            raise errors.ReadOnlyError(self)
144
        else:
145
            self._lock_count +=1
146
        self.branch.lock_write()
147
        return lock.LogicalLockResult(self.unlock)
148
149
    def is_locked(self):
150
        return self._lock_count >= 1
151
152
    def get_physical_lock_status(self):
153
        return False
154
155
    def unlock(self):
156
        if not self._lock_count:
157
            return lock.cant_unlock_not_held(self)
0.200.1530 by Jelmer Vernooij
Fix lock order.
158
        self.branch.unlock()
0.200.1476 by Jelmer Vernooij
Cope with working tree refactoring.
159
        self._cleanup()
160
        self._lock_count -= 1
161
        if self._lock_count > 0:
162
            return
163
        self._lock_mode = None
0.200.173 by Jelmer Vernooij
Merge changes, open index.
164
0.200.1658 by Jelmer Vernooij
Fix handling of ignores - return patterns that matched.
165
    def _cleanup(self):
166
        pass
167
0.200.1322 by Jelmer Vernooij
Add case detection.
168
    def _detect_case_handling(self):
169
        try:
170
            self._transport.stat(".git/cOnFiG")
171
        except errors.NoSuchFile:
172
            self.case_sensitive = True
173
        else:
174
            self.case_sensitive = False
175
0.200.1315 by Jelmer Vernooij
Implement WorkingTree.merge_modified.
176
    def merge_modified(self):
177
        return {}
178
0.200.1696 by Jelmer Vernooij
Fix set_merge_modified.
179
    def set_merge_modified(self, modified_hashes):
0.200.1690 by Jelmer Vernooij
Implement WorkingTree.set_merge_modified.
180
        # TODO(jelmer)
181
        pass
182
0.200.1220 by Jelmer Vernooij
Support set_parent_trees.
183
    def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
184
        self.set_parent_ids([p for p, t in parents_list])
185
0.200.1599 by Jelmer Vernooij
Implement GitWorkingTree.iter_children.
186
    def iter_children(self, file_id):
187
        dpath = self.id2path(file_id) + "/"
188
        if dpath in self.index:
189
            return
190
        for path in self.index:
191
            if not path.startswith(dpath):
192
                continue
193
            if "/" in path[len(dpath):]:
194
                # Not a direct child but something further down
195
                continue
196
            yield self.path2id(path)
197
0.200.1663 by Jelmer Vernooij
Raise SettingFileIdUnsupported
198
    def _index_add_entry(self, path, kind):
0.200.1525 by Jelmer Vernooij
Make sure to always use an up-to-date index.
199
        assert self._lock_mode is not None
0.200.1206 by Jelmer Vernooij
Implement GitWorkingTree.all_file_ids.
200
        assert isinstance(path, basestring)
0.264.2 by Jelmer Vernooij
Implement GitWorkingTree.{_add,__iter__,id2path}.
201
        if kind == "directory":
202
            # Git indexes don't contain directories
203
            return
204
        if kind == "file":
205
            blob = Blob()
206
            try:
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
207
                file, stat_val = self.get_file_with_stat(path)
0.264.2 by Jelmer Vernooij
Implement GitWorkingTree.{_add,__iter__,id2path}.
208
            except (errors.NoSuchFile, IOError):
209
                # TODO: Rather than come up with something here, use the old index
210
                file = StringIO()
0.265.1 by Martin
Don't import posix module, the os wrapper exists for portability
211
                stat_val = os.stat_result(
212
                    (stat.S_IFREG | 0644, 0, 0, 0, 0, 0, 0, 0, 0, 0))
0.264.2 by Jelmer Vernooij
Implement GitWorkingTree.{_add,__iter__,id2path}.
213
            blob.set_raw_string(file.read())
214
        elif kind == "symlink":
215
            blob = Blob()
216
            try:
217
                stat_val = os.lstat(self.abspath(path))
218
            except (errors.NoSuchFile, OSError):
0.200.1636 by Jelmer Vernooij
Some formatting fixes.
219
                # TODO: Rather than come up with something here, use the
0.264.2 by Jelmer Vernooij
Implement GitWorkingTree.{_add,__iter__,id2path}.
220
                # old index
0.265.1 by Martin
Don't import posix module, the os wrapper exists for portability
221
                stat_val = os.stat_result(
222
                    (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.
223
            blob.set_raw_string(
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
224
                self.get_symlink_target(path).encode("utf-8"))
0.264.2 by Jelmer Vernooij
Implement GitWorkingTree.{_add,__iter__,id2path}.
225
        else:
226
            raise AssertionError("unknown kind '%s'" % kind)
227
        # Add object to the repository if it didn't exist yet
0.200.1205 by Jelmer Vernooij
Implement GitWorkingTree.stored_kind.
228
        if not blob.id in self.store:
229
            self.store.add_object(blob)
0.264.2 by Jelmer Vernooij
Implement GitWorkingTree.{_add,__iter__,id2path}.
230
        # Add an entry to the index or update the existing entry
231
        flags = 0 # FIXME
0.200.1242 by Jelmer Vernooij
Support directories better.
232
        encoded_path = path.encode("utf-8")
0.200.1531 by Jelmer Vernooij
Don't trust index contents - verify against file timestamps.
233
        self.index[encoded_path] = index_entry_from_stat(
234
            stat_val, blob.id, flags)
0.200.1242 by Jelmer Vernooij
Support directories better.
235
        if self._versioned_dirs is not None:
236
            self._ensure_versioned_dir(encoded_path)
237
238
    def _ensure_versioned_dir(self, dirname):
0.200.1249 by Jelmer Vernooij
Fix file id for tree root
239
        if dirname in self._versioned_dirs:
0.200.1242 by Jelmer Vernooij
Support directories better.
240
            return
0.200.1249 by Jelmer Vernooij
Fix file id for tree root
241
        if dirname != "":
242
            self._ensure_versioned_dir(posixpath.dirname(dirname))
0.200.1242 by Jelmer Vernooij
Support directories better.
243
        self._versioned_dirs.add(dirname)
244
245
    def _load_dirs(self):
0.200.1525 by Jelmer Vernooij
Make sure to always use an up-to-date index.
246
        assert self._lock_mode is not None
0.200.1242 by Jelmer Vernooij
Support directories better.
247
        self._versioned_dirs = set()
248
        for p in self.index:
249
            self._ensure_versioned_dir(posixpath.dirname(p))
0.264.2 by Jelmer Vernooij
Implement GitWorkingTree.{_add,__iter__,id2path}.
250
0.200.1215 by Jelmer Vernooij
Implement GitWorkingTree.remove.
251
    def _unversion_path(self, path):
0.200.1525 by Jelmer Vernooij
Make sure to always use an up-to-date index.
252
        assert self._lock_mode is not None
0.200.1215 by Jelmer Vernooij
Implement GitWorkingTree.remove.
253
        encoded_path = path.encode("utf-8")
254
        try:
255
            del self.index[encoded_path]
256
        except KeyError:
257
            # A directory, perhaps?
258
            for p in list(self.index):
0.200.1692 by Jelmer Vernooij
Mark three more tests as xfail.
259
                if p.startswith(encoded_path+b"/"):
0.200.1215 by Jelmer Vernooij
Implement GitWorkingTree.remove.
260
                    del self.index[p]
0.200.1242 by Jelmer Vernooij
Support directories better.
261
        # FIXME: remove empty directories
0.200.1215 by Jelmer Vernooij
Implement GitWorkingTree.remove.
262
0.285.8 by Jelmer Vernooij
Fix more tests for swapped arguments.
263
    def unversion(self, paths, file_ids=None):
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
264
        with self.lock_tree_write():
0.200.1690 by Jelmer Vernooij
Implement WorkingTree.set_merge_modified.
265
            for path in paths:
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
266
                self._unversion_path(path)
267
            self.flush()
0.200.1215 by Jelmer Vernooij
Implement GitWorkingTree.remove.
268
0.200.1678 by Jelmer Vernooij
Fix tests.
269
    def update_basis_by_delta(self, revid, delta):
270
        # TODO(jelmer): This shouldn't be called, it's inventory specific.
271
        pass
272
0.200.1243 by Jelmer Vernooij
Implement WorkingTree.check_state.
273
    def check_state(self):
274
        """Check that the working state is/isn't valid."""
275
        pass
276
0.200.1215 by Jelmer Vernooij
Implement GitWorkingTree.remove.
277
    def remove(self, files, verbose=False, to_file=None, keep_files=True,
278
        force=False):
279
        """Remove nominated files from the working tree metadata.
280
281
        :param files: File paths relative to the basedir.
282
        :param keep_files: If true, the files will also be kept.
283
        :param force: Delete files and directories, even if they are changed
284
            and even if the directories are not empty.
285
        """
0.200.1636 by Jelmer Vernooij
Some formatting fixes.
286
        all_files = set() # specified and nested files
0.200.1215 by Jelmer Vernooij
Implement GitWorkingTree.remove.
287
288
        if isinstance(files, basestring):
289
            files = [files]
290
291
        if to_file is None:
292
            to_file = sys.stdout
293
294
        files = list(all_files)
295
296
        if len(files) == 0:
297
            return # nothing to do
298
299
        # Sort needed to first handle directory content before the directory
300
        files.sort(reverse=True)
301
302
        def backup(file_to_backup):
303
            abs_path = self.abspath(file_to_backup)
0.200.1648 by Jelmer Vernooij
Fix compatibility with newer versions of breezy.
304
            backup_name = self.controldir._available_backup_name(file_to_backup)
0.200.1215 by Jelmer Vernooij
Implement GitWorkingTree.remove.
305
            osutils.rename(abs_path, self.abspath(backup_name))
306
            return "removed %s (but kept a copy: %s)" % (
307
                file_to_backup, backup_name)
308
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
309
        with self.lock_tree_write():
310
            for f in files:
311
                fid = self.path2id(f)
312
                if not fid:
313
                    message = "%s is not versioned." % (f,)
314
                else:
315
                    abs_path = self.abspath(f)
316
                    if verbose:
317
                        # having removed it, it must be either ignored or unknown
318
                        if self.is_ignored(f):
319
                            new_status = 'I'
320
                        else:
321
                            new_status = '?'
322
                        # XXX: Really should be a more abstract reporter interface
323
                        kind_ch = osutils.kind_marker(self.kind(fid))
324
                        to_file.write(new_status + '       ' + f + kind_ch + '\n')
325
                    # Unversion file
326
                    # FIXME: _unversion_path() is O(size-of-index) for directories
327
                    self._unversion_path(f)
328
                    message = "removed %s" % (f,)
329
                    if osutils.lexists(abs_path):
330
                        if (osutils.isdir(abs_path) and
331
                            len(os.listdir(abs_path)) > 0):
332
                            if force:
333
                                osutils.rmtree(abs_path)
334
                                message = "deleted %s" % (f,)
335
                            else:
336
                                message = backup(f)
337
                        else:
338
                            if not keep_files:
339
                                osutils.delete_any(abs_path)
340
                                message = "deleted %s" % (f,)
0.200.1215 by Jelmer Vernooij
Implement GitWorkingTree.remove.
341
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
342
                # print only one message (if any) per file.
343
                if message is not None:
344
                    trace.note(message)
345
            self.flush()
0.200.1192 by Jelmer Vernooij
Implement path2id.
346
0.264.2 by Jelmer Vernooij
Implement GitWorkingTree.{_add,__iter__,id2path}.
347
    def _add(self, files, ids, kinds):
348
        for (path, file_id, kind) in zip(files, ids, kinds):
0.200.1201 by Jelmer Vernooij
Implement _set_root_id.
349
            if file_id is not None:
0.200.1663 by Jelmer Vernooij
Raise SettingFileIdUnsupported
350
                raise workingtree.SettingFileIdUnsupported()
351
            self._index_add_entry(path, kind)
0.200.1201 by Jelmer Vernooij
Implement _set_root_id.
352
0.200.1240 by Jelmer Vernooij
Implement GitWorkingTree.smart_add.
353
    def smart_add(self, file_list, recurse=True, action=None, save=True):
354
        added = []
355
        ignored = {}
356
        user_dirs = []
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
357
        with self.lock_tree_write():
358
            for filepath in osutils.canonical_relpaths(self.basedir, file_list):
359
                abspath = self.abspath(filepath)
0.200.1240 by Jelmer Vernooij
Implement GitWorkingTree.smart_add.
360
                kind = osutils.file_kind(abspath)
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
361
                if action is not None:
362
                    file_id = action(self, None, filepath, kind)
0.200.1240 by Jelmer Vernooij
Implement GitWorkingTree.smart_add.
363
                else:
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
364
                    file_id = None
365
                if kind in ("file", "symlink"):
0.200.1308 by Jelmer Vernooij
Write index to disk after adding files.
366
                    if save:
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
367
                        self._index_add_entry(filepath, kind)
368
                    added.append(filepath)
369
                elif kind == "directory":
370
                    if recurse:
371
                        user_dirs.append(filepath)
372
                else:
373
                    raise errors.BadFileKindError(filename=abspath, kind=kind)
374
            for user_dir in user_dirs:
375
                abs_user_dir = self.abspath(user_dir)
376
                for name in os.listdir(abs_user_dir):
377
                    subp = os.path.join(user_dir, name)
378
                    if self.is_control_filename(subp) or self.mapping.is_special_file(subp):
379
                        continue
380
                    ignore_glob = self.is_ignored(subp)
381
                    if ignore_glob is not None:
382
                        ignored.setdefault(ignore_glob, []).append(subp)
383
                        continue
384
                    abspath = self.abspath(subp)
385
                    kind = osutils.file_kind(abspath)
386
                    if kind == "directory":
387
                        user_dirs.append(subp)
388
                    else:
389
                        if action is not None:
390
                            file_id = action(self, None, filepath, kind)
391
                        else:
392
                            file_id = None
393
                        if save:
394
                            self._index_add_entry(subp, kind)
395
            if added and save:
396
                self.flush()
397
            return added, ignored
0.200.1240 by Jelmer Vernooij
Implement GitWorkingTree.smart_add.
398
0.200.1201 by Jelmer Vernooij
Implement _set_root_id.
399
    def _set_root_id(self, file_id):
400
        self._fileid_map.set_file_id("", file_id)
0.264.2 by Jelmer Vernooij
Implement GitWorkingTree.{_add,__iter__,id2path}.
401
0.200.1193 by Jelmer Vernooij
Implement GitWorkingTree.{move,rename_one}.
402
    def move(self, from_paths, to_dir=None, after=False):
403
        rename_tuples = []
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
404
        with self.lock_tree_write():
405
            to_abs = self.abspath(to_dir)
406
            if not os.path.isdir(to_abs):
407
                raise errors.BzrMoveFailedError('', to_dir,
408
                    errors.NotADirectory(to_abs))
409
410
            for from_rel in from_paths:
411
                from_tail = os.path.split(from_rel)[-1]
412
                to_rel = os.path.join(to_dir, from_tail)
413
                self.rename_one(from_rel, to_rel, after=after)
414
                rename_tuples.append((from_rel, to_rel))
415
            self.flush()
416
            return rename_tuples
417
0.200.1193 by Jelmer Vernooij
Implement GitWorkingTree.{move,rename_one}.
418
    def rename_one(self, from_rel, to_rel, after=False):
0.200.1203 by Jelmer Vernooij
Fix per_workingtree.test_rename_one.TestRenameOne.test_rename_after_non_existant_non_ascii
419
        from_path = from_rel.encode("utf-8")
420
        to_path = to_rel.encode("utf-8")
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
421
        with self.lock_tree_write():
422
            if not self.has_filename(to_rel):
423
                raise errors.BzrMoveFailedError(from_rel, to_rel,
424
                    errors.NoSuchFile(to_rel))
425
            if not from_path in self.index:
426
                raise errors.BzrMoveFailedError(from_rel, to_rel,
427
                    errors.NotVersionedError(path=from_rel))
428
            if not after:
429
                os.rename(self.abspath(from_rel), self.abspath(to_rel))
430
            self.index[to_path] = self.index[from_path]
431
            del self.index[from_path]
432
            self.flush()
0.200.1193 by Jelmer Vernooij
Implement GitWorkingTree.{move,rename_one}.
433
0.264.1 by Jelmer Vernooij
Provide stubs using inventory for the moment.:
434
    def get_root_id(self):
0.200.1192 by Jelmer Vernooij
Implement path2id.
435
        return self.path2id("")
436
0.200.1712 by Jelmer Vernooij
Add file_id prefix.
437
    def has_filename(self, filename):
438
        return osutils.lexists(self.abspath(filename))
439
0.200.1242 by Jelmer Vernooij
Support directories better.
440
    def _has_dir(self, path):
0.200.1368 by Jelmer Vernooij
There is always a tree root.
441
        if path == "":
442
            return True
0.200.1242 by Jelmer Vernooij
Support directories better.
443
        if self._versioned_dirs is None:
444
            self._load_dirs()
445
        return path in self._versioned_dirs
446
0.200.1192 by Jelmer Vernooij
Implement path2id.
447
    def path2id(self, path):
0.200.1600 by Jelmer Vernooij
Cope with paths as lists of elements.
448
        if type(path) is list:
449
            path = u"/".join(path)
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
450
        with self.lock_read():
451
            encoded_path = path.encode("utf-8")
452
            if self._is_versioned(encoded_path):
453
                return self._fileid_map.lookup_file_id(encoded_path)
454
            return None
0.264.1 by Jelmer Vernooij
Provide stubs using inventory for the moment.:
455
0.200.1328 by Jelmer Vernooij
More test fixes.
456
    def _iter_files_recursive(self, from_dir=None):
457
        if from_dir is None:
458
            from_dir = ""
459
        for (dirpath, dirnames, filenames) in os.walk(self.abspath(from_dir)):
0.200.1302 by Jelmer Vernooij
Significantly improve performance of WorkingTree.extras().
460
            dir_relpath = dirpath[len(self.basedir):].strip("/")
0.200.1648 by Jelmer Vernooij
Fix compatibility with newer versions of breezy.
461
            if self.controldir.is_control_filename(dir_relpath):
0.200.605 by Jelmer Vernooij
Ignore directories in WorkingTree.extras().
462
                continue
0.200.615 by Jelmer Vernooij
Optimize WorkingTree.extras().
463
            for filename in filenames:
0.200.1328 by Jelmer Vernooij
More test fixes.
464
                if not self.mapping.is_special_file(filename):
465
                    yield os.path.join(dir_relpath, filename)
0.200.1327 by Jelmer Vernooij
Factor out all file browsing in extras.
466
467
    def extras(self):
468
        """Yield all unversioned files in this WorkingTree.
469
        """
0.200.1676 by Jelmer Vernooij
Fix typo.
470
        with self.lock_read():
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
471
            return set(self._iter_files_recursive()) - set(self.index)
0.200.605 by Jelmer Vernooij
Ignore directories in WorkingTree.extras().
472
0.200.382 by Jelmer Vernooij
Support flushing index.
473
    def flush(self):
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
474
        with self.lock_tree_write():
475
            # TODO: Maybe this should only write on dirty ?
476
            if self._lock_mode != 'w':
477
                raise errors.NotWriteLocked(self)
478
            self.index.write()
0.200.382 by Jelmer Vernooij
Support flushing index.
479
0.264.1 by Jelmer Vernooij
Provide stubs using inventory for the moment.:
480
    def __iter__(self):
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
481
        with self.lock_read():
482
            for path in self.index:
483
                yield self.path2id(path)
484
            self._load_dirs()
485
            for path in self._versioned_dirs:
486
                yield self.path2id(path)
0.264.1 by Jelmer Vernooij
Provide stubs using inventory for the moment.:
487
0.200.1202 by Jelmer Vernooij
Implement has_or_had_id.
488
    def has_or_had_id(self, file_id):
489
        if self.has_id(file_id):
490
            return True
491
        if self.had_id(file_id):
492
            return True
493
        return False
494
495
    def had_id(self, file_id):
496
        path = self._basis_fileid_map.lookup_file_id(file_id)
497
        try:
498
            head = self.repository._git.head()
499
        except KeyError:
500
            # Assume no if basis is not accessible
501
            return False
0.200.1205 by Jelmer Vernooij
Implement GitWorkingTree.stored_kind.
502
        if head == ZERO_SHA:
503
            return False
0.200.1202 by Jelmer Vernooij
Implement has_or_had_id.
504
        root_tree = self.store[head].tree
505
        try:
506
            tree_lookup_path(self.store.__getitem__, root_tree, path)
507
        except KeyError:
508
            return False
509
        else:
510
            return True
511
0.200.1198 by Jelmer Vernooij
Implement GitWorkingTree.has_id.
512
    def has_id(self, file_id):
513
        try:
514
            self.id2path(file_id)
515
        except errors.NoSuchId:
516
            return False
517
        else:
518
            return True
519
0.264.1 by Jelmer Vernooij
Provide stubs using inventory for the moment.:
520
    def id2path(self, file_id):
0.200.1532 by Jelmer Vernooij
Cope with float timestamps.
521
        assert type(file_id) is str, "file id not a string: %r" % file_id
0.200.1411 by Jelmer Vernooij
Fix control files.
522
        file_id = osutils.safe_utf8(file_id)
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
523
        with self.lock_read():
0.200.1712 by Jelmer Vernooij
Add file_id prefix.
524
            try:
525
                path = self._fileid_map.lookup_path(file_id)
526
            except ValueError:
527
                raise errors.NoSuchId(self, file_id)
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
528
            # FIXME: What about directories?
529
            if self._is_versioned(path):
530
                return path.decode("utf-8")
531
            raise errors.NoSuchId(self, file_id)
0.264.1 by Jelmer Vernooij
Provide stubs using inventory for the moment.:
532
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
533
    def get_file_mtime(self, path, file_id=None):
0.200.1200 by Jelmer Vernooij
Support GitWorkingTree.get_file_mtime.
534
        """See Tree.get_file_mtime."""
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
535
        try:
536
            return os.lstat(self.abspath(path)).st_mtime
537
        except OSError, (num, msg):
538
            if num == errno.ENOENT:
539
                raise errors.NoSuchFile(path)
540
            raise
0.200.1200 by Jelmer Vernooij
Support GitWorkingTree.get_file_mtime.
541
0.200.1655 by Jelmer Vernooij
Basic support for git ignores.
542
    def is_ignored(self, filename):
543
        r"""Check whether the filename matches an ignore pattern.
544
545
        If the file is ignored, returns the pattern which caused it to
546
        be ignored, otherwise None.  So this can simply be used as a
547
        boolean if desired."""
548
        if getattr(self, '_global_ignoreglobster', None) is None:
549
            ignore_globs = set()
550
            ignore_globs.update(ignores.get_runtime_ignores())
551
            ignore_globs.update(ignores.get_user_ignores())
552
            self._global_ignoreglobster = globbing.ExceptionGlobster(ignore_globs)
0.200.1656 by Jelmer Vernooij
Report proper patterns, ignore files.
553
        match = self._global_ignoreglobster.match(filename)
554
        if match is not None:
555
            return match
0.200.1655 by Jelmer Vernooij
Basic support for git ignores.
556
        if osutils.file_kind(self.abspath(filename)) == 'directory':
557
            filename += b'/'
0.200.1658 by Jelmer Vernooij
Fix handling of ignores - return patterns that matched.
558
        ignore_manager = self._get_ignore_manager()
559
        ps = list(ignore_manager.find_matching(filename))
560
        if not ps:
561
            return None
562
        if not ps[-1].is_exclude:
563
            return None
564
        return bytes(ps[-1])
565
566
    def _get_ignore_manager(self):
567
        ignoremanager = getattr(self, '_ignoremanager', None)
568
        if ignoremanager is not None:
569
            return ignoremanager
570
571
        ignore_manager = IgnoreFilterManager.from_repo(self.repository._git)
572
        self._ignoremanager = ignore_manager
573
        return ignore_manager
0.200.409 by Jelmer Vernooij
Support parsing .gitignore.
574
0.200.508 by Jelmer Vernooij
Skip inventory caching bits.
575
    def set_last_revision(self, revid):
576
        self._change_last_revision(revid)
577
0.200.379 by Jelmer Vernooij
Re-enable working tree support.
578
    def _reset_data(self):
0.248.3 by Jelmer Vernooij
Handle working trees without valid HEAD branch.
579
        try:
580
            head = self.repository._git.head()
581
        except KeyError, name:
0.200.1308 by Jelmer Vernooij
Write index to disk after adding files.
582
            raise errors.NotBranchError("branch %s at %s" % (name,
583
                self.repository.base))
0.200.948 by Jelmer Vernooij
Cope with empty inventories.
584
        if head == ZERO_SHA:
0.200.1202 by Jelmer Vernooij
Implement has_or_had_id.
585
            self._basis_fileid_map = GitFileIdMap({}, self.mapping)
0.200.948 by Jelmer Vernooij
Cope with empty inventories.
586
        else:
0.200.1308 by Jelmer Vernooij
Write index to disk after adding files.
587
            self._basis_fileid_map = self.mapping.get_fileid_map(
588
                self.store.__getitem__, self.store[head].tree)
0.200.379 by Jelmer Vernooij
Re-enable working tree support.
589
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
590
    def get_file_verifier(self, path, file_id=None, stat_value=None):
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
591
        with self.lock_read():
592
            try:
593
                return ("GIT", self.index[path][-2])
594
            except KeyError:
595
                if self._has_dir(path):
596
                    return ("GIT", None)
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
597
                raise errors.NoSuchFile(path)
0.200.1302 by Jelmer Vernooij
Significantly improve performance of WorkingTree.extras().
598
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
599
    def get_file_sha1(self, path, file_id=None, stat_value=None):
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
600
        with self.lock_read():
601
            abspath = self.abspath(path).encode(osutils._fs_enc)
602
            try:
603
                return osutils.sha_file_by_name(abspath)
604
            except OSError, (num, msg):
605
                if num in (errno.EISDIR, errno.ENOENT):
606
                    return None
607
                raise
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
608
0.200.610 by Jelmer Vernooij
Support retrieving basis tree properly.
609
    def revision_tree(self, revid):
610
        return self.repository.revision_tree(revid)
611
0.200.1242 by Jelmer Vernooij
Support directories better.
612
    def _is_versioned(self, path):
0.200.1525 by Jelmer Vernooij
Make sure to always use an up-to-date index.
613
        assert self._lock_mode is not None
0.200.1242 by Jelmer Vernooij
Support directories better.
614
        return (path in self.index or self._has_dir(path))
615
0.200.1239 by Jelmer Vernooij
Implement GitWorkingTree.filter_unversioned_files.
616
    def filter_unversioned_files(self, files):
0.200.1316 by Jelmer Vernooij
Fix filter_unversioned_files.
617
        return set([p for p in files if not self._is_versioned(p.encode("utf-8"))])
0.200.1239 by Jelmer Vernooij
Implement GitWorkingTree.filter_unversioned_files.
618
0.264.11 by Jelmer Vernooij
Completer implementation of iter_entries_by_dir and list_files.
619
    def _get_dir_ie(self, path, parent_id):
0.200.1192 by Jelmer Vernooij
Implement path2id.
620
        file_id = self.path2id(path)
0.264.11 by Jelmer Vernooij
Completer implementation of iter_entries_by_dir and list_files.
621
        return inventory.InventoryDirectory(file_id,
0.200.1190 by Jelmer Vernooij
Fix get_symlink_target call.
622
            posixpath.basename(path).strip("/"), parent_id)
0.264.11 by Jelmer Vernooij
Completer implementation of iter_entries_by_dir and list_files.
623
624
    def _add_missing_parent_ids(self, path, dir_ids):
625
        if path in dir_ids:
626
            return []
627
        parent = posixpath.dirname(path).strip("/")
628
        ret = self._add_missing_parent_ids(parent, dir_ids)
629
        parent_id = dir_ids[parent]
630
        ie = self._get_dir_ie(path, parent_id)
631
        dir_ids[path] = ie.file_id
632
        ret.append((path, ie))
633
        return ret
634
0.200.1321 by Jelmer Vernooij
More fixes for compatibility with bzr.dev testsuite.
635
    def _get_file_ie(self, name, path, value, parent_id):
636
        assert isinstance(name, unicode)
0.200.1192 by Jelmer Vernooij
Implement path2id.
637
        assert isinstance(path, unicode)
0.264.10 by Jelmer Vernooij
Yield inventory entries.
638
        assert isinstance(value, tuple) and len(value) == 10
639
        (ctime, mtime, dev, ino, mode, uid, gid, size, sha, flags) = value
0.200.1192 by Jelmer Vernooij
Implement path2id.
640
        file_id = self.path2id(path)
0.264.10 by Jelmer Vernooij
Yield inventory entries.
641
        if type(file_id) != str:
642
            raise AssertionError
643
        kind = mode_kind(mode)
0.200.1321 by Jelmer Vernooij
More fixes for compatibility with bzr.dev testsuite.
644
        ie = inventory.entry_factory[kind](file_id, name, parent_id)
0.264.10 by Jelmer Vernooij
Yield inventory entries.
645
        if kind == 'symlink':
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
646
            ie.symlink_target = self.get_symlink_target(path, file_id)
0.264.10 by Jelmer Vernooij
Yield inventory entries.
647
        else:
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
648
            data = self.get_file_text(path, file_id)
0.264.10 by Jelmer Vernooij
Yield inventory entries.
649
            ie.text_sha1 = osutils.sha_string(data)
650
            ie.text_size = len(data)
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
651
            ie.executable = self.is_executable(path, file_id)
0.264.10 by Jelmer Vernooij
Yield inventory entries.
652
        ie.revision = None
653
        return ie
654
0.264.11 by Jelmer Vernooij
Completer implementation of iter_entries_by_dir and list_files.
655
    def _is_executable_from_path_and_stat_from_stat(self, path, stat_result):
656
        mode = stat_result.st_mode
657
        return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
658
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
659
    def stored_kind(self, path, file_id=None):
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
660
        with self.lock_read():
661
            try:
662
                return mode_kind(self.index[path.encode("utf-8")][4])
663
            except KeyError:
664
                # Maybe it's a directory?
665
                if self._has_dir(path):
666
                    return "directory"
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
667
                raise errors.NoSuchFile(path)
0.200.1205 by Jelmer Vernooij
Implement GitWorkingTree.stored_kind.
668
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
669
    def is_executable(self, path, file_id=None):
0.200.1539 by Jelmer Vernooij
Cope with new is_executable.
670
        if getattr(self, "_supports_executable", osutils.supports_executable)():
671
            mode = os.lstat(self.abspath(path)).st_mode
672
            return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
673
        else:
0.264.11 by Jelmer Vernooij
Completer implementation of iter_entries_by_dir and list_files.
674
            basis_tree = self.basis_tree()
675
            if file_id in basis_tree:
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
676
                return basis_tree.is_executable(path, file_id)
0.264.11 by Jelmer Vernooij
Completer implementation of iter_entries_by_dir and list_files.
677
            # Default to not executable
678
            return False
679
0.200.1539 by Jelmer Vernooij
Cope with new is_executable.
680
    def _is_executable_from_path_and_stat(self, path, stat_result):
681
        if getattr(self, "_supports_executable", osutils.supports_executable)():
682
            return self._is_executable_from_path_and_stat_from_stat(path, stat_result)
683
        else:
684
            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.
685
0.264.10 by Jelmer Vernooij
Yield inventory entries.
686
    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.
687
        if from_dir is None:
688
            from_dir = ""
0.264.11 by Jelmer Vernooij
Completer implementation of iter_entries_by_dir and list_files.
689
        dir_ids = {}
0.200.1339 by Jelmer Vernooij
Some reformatting.
690
        fk_entries = {'directory': workingtree.TreeDirectory,
691
                      'file': workingtree.TreeFile,
692
                      'symlink': workingtree.TreeLink}
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
693
        with self.lock_read():
694
            root_ie = self._get_dir_ie(u"", None)
695
            if include_root and not from_dir:
696
                yield "", "V", root_ie.kind, root_ie.file_id, root_ie
697
            dir_ids[u""] = root_ie.file_id
698
            if recursive:
699
                path_iterator = self._iter_files_recursive(from_dir)
700
            else:
701
                if from_dir is None:
702
                    start = self.basedir
703
                else:
704
                    start = os.path.join(self.basedir, from_dir)
705
                path_iterator = sorted([os.path.join(from_dir, name) for name in
706
                    os.listdir(start) if not self.controldir.is_control_filename(name)
707
                    and not self.mapping.is_special_file(name)])
708
            for path in path_iterator:
709
                try:
710
                    value = self.index[path]
711
                except KeyError:
712
                    value = None
713
                path = path.decode("utf-8")
714
                parent, name = posixpath.split(path)
715
                for dir_path, dir_ie in self._add_missing_parent_ids(parent, dir_ids):
716
                    yield dir_path, "V", dir_ie.kind, dir_ie.file_id, dir_ie
717
                if value is not None:
718
                    ie = self._get_file_ie(name, path, value, dir_ids[parent])
719
                    yield path, "V", ie.kind, ie.file_id, ie
720
                else:
721
                    kind = osutils.file_kind(self.abspath(path))
722
                    ie = fk_entries[kind]()
723
                    yield path, ("I" if self.is_ignored(path) else "?"), kind, None, ie
0.264.10 by Jelmer Vernooij
Yield inventory entries.
724
0.200.1206 by Jelmer Vernooij
Implement GitWorkingTree.all_file_ids.
725
    def all_file_ids(self):
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
726
        with self.lock_read():
727
            ids = {u"": self.path2id("")}
728
            for path in self.index:
729
                if self.mapping.is_special_file(path):
730
                    continue
731
                path = path.decode("utf-8")
732
                parent = posixpath.dirname(path).strip("/")
733
                for e in self._add_missing_parent_ids(parent, ids):
734
                    pass
735
                ids[path] = self.path2id(path)
736
            return set(ids.values())
0.200.1206 by Jelmer Vernooij
Implement GitWorkingTree.all_file_ids.
737
0.200.1710 by Jelmer Vernooij
Regenerate xfail.
738
    def all_versioned_paths(self):
739
        with self.lock_read():
740
            paths = {u""}
741
            for path in self.index:
742
                if self.mapping.is_special_file(path):
743
                    continue
744
                path = path.decode("utf-8")
745
                paths.add(path)
746
                while path != "":
747
                    path = posixpath.dirname(path).strip("/")
748
                    if path in paths:
749
                        break
750
                    paths.add(path)
751
            return paths
752
0.200.1374 by Jelmer Vernooij
Implement GitWorkingTree._directory_is_tree_reference.
753
    def _directory_is_tree_reference(self, path):
754
        # FIXME: Check .gitsubmodules for path
755
        return False
756
0.264.9 by Jelmer Vernooij
Implement basic GitWorkingTree.iter_entries_by_dir.
757
    def iter_entries_by_dir(self, specific_file_ids=None, yield_parents=False):
758
        # FIXME: Is return order correct?
0.200.1252 by Jelmer Vernooij
Support specific_file_ids in GitWorkingTree.iter_entries_by_dir.
759
        if yield_parents:
760
            raise NotImplementedError(self.iter_entries_by_dir)
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
761
        with self.lock_read():
762
            if specific_file_ids is not None:
0.200.1712 by Jelmer Vernooij
Add file_id prefix.
763
                specific_paths = []
764
                for file_id in specific_file_ids:
765
                    assert file_id is not None, "file id %r" % file_id
766
                    specific_paths.append(self.id2path(file_id))
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
767
                if specific_paths in ([u""], []):
768
                    specific_paths = None
769
                else:
770
                    specific_paths = set(specific_paths)
771
            else:
0.200.1252 by Jelmer Vernooij
Support specific_file_ids in GitWorkingTree.iter_entries_by_dir.
772
                specific_paths = None
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
773
            root_ie = self._get_dir_ie(u"", None)
774
            if specific_paths is None:
775
                yield u"", root_ie
776
            dir_ids = {u"": root_ie.file_id}
777
            for path, value in self.index.iteritems():
778
                if self.mapping.is_special_file(path):
779
                    continue
780
                path = path.decode("utf-8")
781
                if specific_paths is not None and not path in specific_paths:
782
                    continue
783
                (parent, name) = posixpath.split(path)
784
                try:
785
                    file_ie = self._get_file_ie(name, path, value, None)
786
                except IOError:
787
                    continue
788
                for (dir_path, dir_ie) in self._add_missing_parent_ids(parent,
789
                        dir_ids):
790
                    yield dir_path, dir_ie
791
                file_ie.parent_id = self.path2id(parent)
792
                yield path, file_ie
0.264.9 by Jelmer Vernooij
Implement basic GitWorkingTree.iter_entries_by_dir.
793
0.200.619 by Jelmer Vernooij
Provide dummy WorkingTree.conflicts() implementation rather than spending a lot of time not finding any conflicts.
794
    def conflicts(self):
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
795
        with self.lock_read():
796
            # FIXME:
797
            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.
798
0.200.1328 by Jelmer Vernooij
More test fixes.
799
    def get_canonical_inventory_path(self, path):
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
800
        with self.lock_read():
801
            for p in self.index:
802
                if p.lower() == path.lower():
803
                    return p
804
            else:
805
                return path
0.200.1328 by Jelmer Vernooij
More test fixes.
806
0.200.1705 by Jelmer Vernooij
Fix walkdirs.
807
    def walkdirs(self, prefix=""):
808
        """Walk the directories of this tree.
809
810
        returns a generator which yields items in the form:
811
                ((curren_directory_path, fileid),
812
                 [(file1_path, file1_name, file1_kind, (lstat), file1_id,
813
                   file1_kind), ... ])
814
815
        This API returns a generator, which is only valid during the current
816
        tree transaction - within a single lock_read or lock_write duration.
817
818
        If the tree is not locked, it may cause an error to be raised,
819
        depending on the tree implementation.
820
        """
821
        disk_top = self.abspath(prefix)
822
        if disk_top.endswith('/'):
823
            disk_top = disk_top[:-1]
824
        top_strip_len = len(disk_top) + 1
825
        inventory_iterator = self._walkdirs(prefix)
826
        disk_iterator = osutils.walkdirs(disk_top, prefix)
827
        try:
828
            current_disk = next(disk_iterator)
829
            disk_finished = False
830
        except OSError as e:
831
            if not (e.errno == errno.ENOENT or
832
                (sys.platform == 'win32' and e.errno == ERROR_PATH_NOT_FOUND)):
833
                raise
834
            current_disk = None
835
            disk_finished = True
836
        try:
837
            current_inv = next(inventory_iterator)
838
            inv_finished = False
839
        except StopIteration:
840
            current_inv = None
841
            inv_finished = True
842
        while not inv_finished or not disk_finished:
843
            if current_disk:
844
                ((cur_disk_dir_relpath, cur_disk_dir_path_from_top),
845
                    cur_disk_dir_content) = current_disk
846
            else:
847
                ((cur_disk_dir_relpath, cur_disk_dir_path_from_top),
848
                    cur_disk_dir_content) = ((None, None), None)
849
            if not disk_finished:
850
                # strip out .bzr dirs
851
                if (cur_disk_dir_path_from_top[top_strip_len:] == '' and
852
                    len(cur_disk_dir_content) > 0):
853
                    # osutils.walkdirs can be made nicer -
854
                    # yield the path-from-prefix rather than the pathjoined
855
                    # value.
856
                    bzrdir_loc = bisect_left(cur_disk_dir_content,
857
                        ('.bzr', '.bzr'))
858
                    if (bzrdir_loc < len(cur_disk_dir_content)
859
                        and self.controldir.is_control_filename(
860
                            cur_disk_dir_content[bzrdir_loc][0])):
861
                        # we dont yield the contents of, or, .bzr itself.
862
                        del cur_disk_dir_content[bzrdir_loc]
863
            if inv_finished:
864
                # everything is unknown
865
                direction = 1
866
            elif disk_finished:
867
                # everything is missing
868
                direction = -1
869
            else:
870
                direction = cmp(current_inv[0][0], cur_disk_dir_relpath)
871
            if direction > 0:
872
                # disk is before inventory - unknown
873
                dirblock = [(relpath, basename, kind, stat, None, None) for
874
                    relpath, basename, kind, stat, top_path in
875
                    cur_disk_dir_content]
876
                yield (cur_disk_dir_relpath, None), dirblock
877
                try:
878
                    current_disk = next(disk_iterator)
879
                except StopIteration:
880
                    disk_finished = True
881
            elif direction < 0:
882
                # inventory is before disk - missing.
883
                dirblock = [(relpath, basename, 'unknown', None, fileid, kind)
884
                    for relpath, basename, dkind, stat, fileid, kind in
885
                    current_inv[1]]
886
                yield (current_inv[0][0], current_inv[0][1]), dirblock
887
                try:
888
                    current_inv = next(inventory_iterator)
889
                except StopIteration:
890
                    inv_finished = True
891
            else:
892
                # versioned present directory
893
                # merge the inventory and disk data together
894
                dirblock = []
895
                for relpath, subiterator in itertools.groupby(sorted(
896
                    current_inv[1] + cur_disk_dir_content,
897
                    key=operator.itemgetter(0)), operator.itemgetter(1)):
898
                    path_elements = list(subiterator)
899
                    if len(path_elements) == 2:
900
                        inv_row, disk_row = path_elements
901
                        # versioned, present file
902
                        dirblock.append((inv_row[0],
903
                            inv_row[1], disk_row[2],
904
                            disk_row[3], inv_row[4],
905
                            inv_row[5]))
906
                    elif len(path_elements[0]) == 5:
907
                        # unknown disk file
908
                        dirblock.append((path_elements[0][0],
909
                            path_elements[0][1], path_elements[0][2],
910
                            path_elements[0][3], None, None))
911
                    elif len(path_elements[0]) == 6:
912
                        # versioned, absent file.
913
                        dirblock.append((path_elements[0][0],
914
                            path_elements[0][1], 'unknown', None,
915
                            path_elements[0][4], path_elements[0][5]))
916
                    else:
917
                        raise NotImplementedError('unreachable code')
918
                yield current_inv[0], dirblock
919
                try:
920
                    current_inv = next(inventory_iterator)
921
                except StopIteration:
922
                    inv_finished = True
923
                try:
924
                    current_disk = next(disk_iterator)
925
                except StopIteration:
926
                    disk_finished = True
927
928
    def walkdirs(self, prefix=""):
0.200.1210 by Jelmer Vernooij
Implement GitWorkingTree._walkdirs.
929
        if prefix != "":
930
            prefix += "/"
931
        per_dir = defaultdict(list)
932
        for path, value in self.index.iteritems():
0.200.1328 by Jelmer Vernooij
More test fixes.
933
            if self.mapping.is_special_file(path):
934
                continue
0.200.1210 by Jelmer Vernooij
Implement GitWorkingTree._walkdirs.
935
            if not path.startswith(prefix):
936
                continue
937
            (dirname, child_name) = posixpath.split(path)
938
            dirname = dirname.decode("utf-8")
939
            dir_file_id = self.path2id(dirname)
940
            assert isinstance(value, tuple) and len(value) == 10
941
            (ctime, mtime, dev, ino, mode, uid, gid, size, sha, flags) = value
0.265.1 by Martin
Don't import posix module, the os wrapper exists for portability
942
            stat_result = os.stat_result((mode, ino,
0.200.1211 by Jelmer Vernooij
Fix statresult.
943
                    dev, 1, uid, gid, size,
944
                    0, mtime, ctime))
0.200.1210 by Jelmer Vernooij
Implement GitWorkingTree._walkdirs.
945
            per_dir[(dirname, dir_file_id)].append(
946
                (path.decode("utf-8"), child_name.decode("utf-8"),
947
                mode_kind(mode), stat_result,
948
                self.path2id(path.decode("utf-8")),
949
                mode_kind(mode)))
950
        return per_dir.iteritems()
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
951
0.200.1542 by Jelmer Vernooij
Refactor iter_changes.
952
    def _lookup_entry(self, path, update_index=False):
0.200.1543 by Jelmer Vernooij
Support symlinks.
953
        assert type(path) == str
0.200.1542 by Jelmer Vernooij
Refactor iter_changes.
954
        entry = self.index[path]
955
        index_mode = entry[-6]
956
        index_sha = entry[-2]
957
        disk_path = os.path.join(self.basedir, path)
0.284.1 by Jelmer Vernooij
Raise KeyError when file was removed.
958
        try:
959
            disk_stat = os.lstat(disk_path)
960
        except OSError, (num, msg):
961
            if num in (errno.EISDIR, errno.ENOENT):
962
                raise KeyError(path)
963
            raise
0.200.1542 by Jelmer Vernooij
Refactor iter_changes.
964
        disk_mtime = disk_stat.st_mtime
965
        if isinstance(entry[1], tuple):
966
            index_mtime = entry[1][0]
967
        else:
968
            index_mtime = int(entry[1])
969
        mtime_delta = (disk_mtime - index_mtime)
970
        disk_mode = cleanup_mode(disk_stat.st_mode)
971
        if mtime_delta > 0 or disk_mode != index_mode:
972
            if stat.S_ISDIR(disk_mode):
973
                try:
974
                    subrepo = Repo(disk_path)
975
                except NotGitRepository:
976
                    return (None, None)
977
                else:
978
                    disk_mode = S_IFGITLINK
979
                    git_id = subrepo.head()
0.200.1543 by Jelmer Vernooij
Support symlinks.
980
            elif stat.S_ISLNK(disk_mode):
0.200.1545 by Jelmer Vernooij
Some more test fixes.
981
                blob = Blob.from_string(os.readlink(disk_path).encode('utf-8'))
0.200.1543 by Jelmer Vernooij
Support symlinks.
982
                git_id = blob.id
983
            elif stat.S_ISREG(disk_mode):
0.200.1542 by Jelmer Vernooij
Refactor iter_changes.
984
                with open(disk_path, 'r') as f:
985
                    blob = Blob.from_string(f.read())
986
                git_id = blob.id
0.200.1543 by Jelmer Vernooij
Support symlinks.
987
            else:
988
                raise AssertionError
0.200.1542 by Jelmer Vernooij
Refactor iter_changes.
989
            if update_index:
990
                flags = 0 # FIXME
0.200.1545 by Jelmer Vernooij
Some more test fixes.
991
                self.index[path] = index_entry_from_stat(disk_stat, git_id, flags, disk_mode)
0.200.1542 by Jelmer Vernooij
Refactor iter_changes.
992
            return (git_id, disk_mode)
993
        return (index_sha, index_mode)
994
0.200.1677 by Jelmer Vernooij
Mark shelving as unsupported.
995
    def get_shelf_manager(self):
996
        raise workingtree.ShelvingUnsupported(self)
997
0.200.1678 by Jelmer Vernooij
Fix tests.
998
    def store_uncommitted(self):
999
        raise errors.StoringUncommittedNotSupported(self)
1000
0.200.1703 by Jelmer Vernooij
Implement apply_inventory_delta.
1001
    def apply_inventory_delta(self, changes):
1002
        for (old_path, new_path, file_id, ie) in changes:
1003
            if old_path is not None:
1004
                del self.index[old_path.encode('utf-8')]
1005
            if new_path is not None and ie.kind != 'directory':
1006
                self._index_add_entry(new_path, ie.kind)
1007
0.200.1308 by Jelmer Vernooij
Write index to disk after adding files.
1008
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
1009
class GitWorkingTreeFormat(workingtree.WorkingTreeFormat):
1010
0.200.1206 by Jelmer Vernooij
Implement GitWorkingTree.all_file_ids.
1011
    _tree_class = GitWorkingTree
1012
0.200.1295 by Jelmer Vernooij
Mark working trees as not supporting directories.
1013
    supports_versioned_directories = False
1014
0.200.1661 by Jelmer Vernooij
Set supports_setting_file_ids to False.
1015
    supports_setting_file_ids = False
1016
0.200.1677 by Jelmer Vernooij
Mark shelving as unsupported.
1017
    supports_store_uncommitted = False
1018
0.200.656 by Jelmer Vernooij
Implement GitWorkingTreeFormat._matchingbzrdir.
1019
    @property
0.200.1665 by Jelmer Vernooij
Rename _matchingbzrdir to _matchingcnotroldir.
1020
    def _matchingcontroldir(self):
0.200.1641 by Jelmer Vernooij
Use relative imports where possible.
1021
        from .dir import LocalGitControlDirFormat
0.200.1013 by Jelmer Vernooij
More renames.
1022
        return LocalGitControlDirFormat()
0.200.656 by Jelmer Vernooij
Implement GitWorkingTreeFormat._matchingbzrdir.
1023
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
1024
    def get_format_description(self):
1025
        return "Git Working Tree"
0.200.616 by Jelmer Vernooij
Provide custom intertree implementation for GitRevisionTree->GitWorkingTree.
1026
0.200.1648 by Jelmer Vernooij
Fix compatibility with newer versions of breezy.
1027
    def initialize(self, a_controldir, revision_id=None, from_branch=None,
0.200.1096 by Jelmer Vernooij
Implement GitWorkingTreeFormat.initialize.
1028
                   accelerator_tree=None, hardlink=False):
1029
        """See WorkingTreeFormat.initialize()."""
0.200.1648 by Jelmer Vernooij
Fix compatibility with newer versions of breezy.
1030
        if not isinstance(a_controldir, LocalGitDir):
1031
            raise errors.IncompatibleFormat(self, a_controldir)
1032
        index = Index(a_controldir.root_transport.local_abspath(".git/index"))
0.200.1096 by Jelmer Vernooij
Implement GitWorkingTreeFormat.initialize.
1033
        index.write()
0.200.1680 by Jelmer Vernooij
Fix repo locks.
1034
        wt = GitWorkingTree(
1035
                a_controldir, a_controldir.open_repository(),
0.200.1648 by Jelmer Vernooij
Fix compatibility with newer versions of breezy.
1036
            a_controldir.open_branch(), index)
0.200.1680 by Jelmer Vernooij
Fix repo locks.
1037
        for hook in MutableTree.hooks['post_build_tree']:
1038
            hook(wt)
1039
        return wt
0.200.1096 by Jelmer Vernooij
Implement GitWorkingTreeFormat.initialize.
1040
0.200.616 by Jelmer Vernooij
Provide custom intertree implementation for GitRevisionTree->GitWorkingTree.
1041
1042
class InterIndexGitTree(tree.InterTree):
1043
    """InterTree that works between a Git revision tree and an index."""
1044
1045
    def __init__(self, source, target):
1046
        super(InterIndexGitTree, self).__init__(source, target)
1047
        self._index = target.index
1048
1049
    @classmethod
1050
    def is_compatible(cls, source, target):
0.200.1641 by Jelmer Vernooij
Use relative imports where possible.
1051
        from .repository import GitRevisionTree
0.200.1636 by Jelmer Vernooij
Some formatting fixes.
1052
        return (isinstance(source, GitRevisionTree) and
0.200.616 by Jelmer Vernooij
Provide custom intertree implementation for GitRevisionTree->GitWorkingTree.
1053
                isinstance(target, GitWorkingTree))
1054
1055
    def compare(self, want_unchanged=False, specific_files=None,
1056
                extra_trees=None, require_versioned=False, include_root=False,
1057
                want_unversioned=False):
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
1058
        with self.lock_read():
1059
            # FIXME: Handle include_root
1060
            changes = changes_between_git_tree_and_index(
1061
                self.source.store, self.source.tree,
1062
                self.target, want_unchanged=want_unchanged,
1063
                want_unversioned=want_unversioned)
1064
            source_fileid_map = self.source._fileid_map
1065
            target_fileid_map = self.target._fileid_map
1066
            ret = tree_delta_from_git_changes(changes, self.target.mapping,
1067
                (source_fileid_map, target_fileid_map),
1068
                specific_file=specific_files, require_versioned=require_versioned)
1069
            if want_unversioned:
1070
                for e in self.target.extras():
1071
                    ret.unversioned.append((e, None,
1072
                        osutils.file_kind(self.target.abspath(e))))
1073
            return ret
0.200.616 by Jelmer Vernooij
Provide custom intertree implementation for GitRevisionTree->GitWorkingTree.
1074
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
1075
    def iter_changes(self, include_unchanged=False, specific_files=None,
0.200.1207 by Jelmer Vernooij
Fix some formatting.
1076
        pb=None, extra_trees=[], require_versioned=True,
1077
        want_unversioned=False):
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
1078
        with self.lock_read():
1079
            changes = changes_between_git_tree_and_index(
1080
                self.source.store, self.source.tree,
1081
                self.target, want_unchanged=include_unchanged,
1082
                want_unversioned=want_unversioned)
1083
            return changes_from_git_changes(
1084
                    changes, self.target.mapping, specific_file=specific_files)
0.200.616 by Jelmer Vernooij
Provide custom intertree implementation for GitRevisionTree->GitWorkingTree.
1085
0.200.1179 by Jelmer Vernooij
Avoid using verifiers for natively imported revisions, save a lot of time.
1086
0.200.616 by Jelmer Vernooij
Provide custom intertree implementation for GitRevisionTree->GitWorkingTree.
1087
tree.InterTree.register_optimiser(InterIndexGitTree)
0.200.1529 by Jelmer Vernooij
Add changes_between_tree_and_index.
1088
1089
0.200.1542 by Jelmer Vernooij
Refactor iter_changes.
1090
def changes_between_git_tree_and_index(object_store, tree, target,
0.200.1529 by Jelmer Vernooij
Add changes_between_tree_and_index.
1091
        want_unchanged=False, want_unversioned=False, update_index=False):
1092
    """Determine the changes between a git tree and a working tree with index.
1093
1094
    """
0.200.1538 by Jelmer Vernooij
More work on tree-reference support.
1095
0.200.1542 by Jelmer Vernooij
Refactor iter_changes.
1096
    names = target.index._byname.keys()
1097
    for (name, mode, sha) in changes_from_tree(names, target._lookup_entry,
0.200.1531 by Jelmer Vernooij
Don't trust index contents - verify against file timestamps.
1098
            object_store, tree, want_unchanged=want_unchanged):
0.200.1538 by Jelmer Vernooij
More work on tree-reference support.
1099
        if name == (None, None):
1100
            continue
0.200.1531 by Jelmer Vernooij
Don't trust index contents - verify against file timestamps.
1101
        yield (name, mode, sha)