/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.381 by Jelmer Vernooij
Support working trees properly, status and ls.
20
0.200.385 by Jelmer Vernooij
Cope with removed files.
21
from cStringIO import (
22
    StringIO,
23
    )
0.200.1210 by Jelmer Vernooij
Implement GitWorkingTree._walkdirs.
24
from collections import defaultdict
0.239.4 by Jelmer Vernooij
Cope with nonexistent files and directories in get_file_sha1.
25
import errno
0.200.1096 by Jelmer Vernooij
Implement GitWorkingTreeFormat.initialize.
26
from dulwich.index import (
27
    Index,
28
    )
0.200.1202 by Jelmer Vernooij
Implement has_or_had_id.
29
from dulwich.object_store import (
30
    tree_lookup_path,
31
    )
0.200.383 by Jelmer Vernooij
Simplify, support rewriting index based on inventory.
32
from dulwich.objects import (
33
    Blob,
0.200.948 by Jelmer Vernooij
Cope with empty inventories.
34
    ZERO_SHA,
35
    )
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
36
import os
0.200.1210 by Jelmer Vernooij
Implement GitWorkingTree._walkdirs.
37
import posix
0.264.10 by Jelmer Vernooij
Yield inventory entries.
38
import posixpath
0.200.384 by Jelmer Vernooij
Fix reading of inventory from index.
39
import stat
0.200.1215 by Jelmer Vernooij
Implement GitWorkingTree.remove.
40
import sys
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
41
42
from bzrlib import (
0.200.382 by Jelmer Vernooij
Support flushing index.
43
    errors,
0.262.1 by Jelmer Vernooij
Fix WorkingTree.conflicts().
44
    conflicts as _mod_conflicts,
0.200.409 by Jelmer Vernooij
Support parsing .gitignore.
45
    ignores,
0.264.10 by Jelmer Vernooij
Yield inventory entries.
46
    inventory,
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
47
    lockable_files,
48
    lockdir,
0.200.381 by Jelmer Vernooij
Support working trees properly, status and ls.
49
    osutils,
0.200.1215 by Jelmer Vernooij
Implement GitWorkingTree.remove.
50
    trace,
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
51
    transport,
0.200.519 by Jelmer Vernooij
Move imports down, might not be available in older bzr-git versions.
52
    tree,
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
53
    workingtree,
54
    )
0.200.381 by Jelmer Vernooij
Support working trees properly, status and ls.
55
from bzrlib.decorators import (
56
    needs_read_lock,
57
    )
0.200.1308 by Jelmer Vernooij
Write index to disk after adding files.
58
from bzrlib.mutabletree import needs_tree_write_lock
0.200.381 by Jelmer Vernooij
Support working trees properly, status and ls.
59
60
0.200.1096 by Jelmer Vernooij
Implement GitWorkingTreeFormat.initialize.
61
from bzrlib.plugins.git.dir import (
62
    LocalGitDir,
63
    )
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
64
from bzrlib.plugins.git.tree import (
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
65
    changes_from_git_changes,
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
66
    tree_delta_from_git_changes,
67
    )
0.200.971 by Chadrik
Fix 'bzr status' after 'bzr add' in native git working trees.
68
from bzrlib.plugins.git.mapping import (
69
    GitFileIdMap,
0.264.10 by Jelmer Vernooij
Yield inventory entries.
70
    mode_kind,
0.200.971 by Chadrik
Fix 'bzr status' after 'bzr add' in native git working trees.
71
    )
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
72
0.200.409 by Jelmer Vernooij
Support parsing .gitignore.
73
IGNORE_FILENAME = ".gitignore"
74
75
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
76
class GitWorkingTree(workingtree.WorkingTree):
77
    """A Git working tree."""
78
0.200.803 by Jelmer Vernooij
Default to non-bare repositories when initializing a control directory.
79
    def __init__(self, bzrdir, repo, branch, index):
0.200.379 by Jelmer Vernooij
Re-enable working tree support.
80
        self.basedir = bzrdir.root_transport.local_abspath('.')
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
81
        self.bzrdir = bzrdir
82
        self.repository = repo
0.200.1205 by Jelmer Vernooij
Implement GitWorkingTree.stored_kind.
83
        self.store = self.repository._git.object_store
0.200.384 by Jelmer Vernooij
Fix reading of inventory from index.
84
        self.mapping = self.repository.get_mapping()
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
85
        self._branch = branch
86
        self._transport = bzrdir.transport
87
0.246.3 by Jelmer Vernooij
Simplify call to abspath, without involving private variables.
88
        self.controldir = self.bzrdir.transport.local_abspath('bzr')
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
89
90
        try:
91
            os.makedirs(self.controldir)
92
            os.makedirs(os.path.join(self.controldir, 'lock'))
93
        except OSError:
94
            pass
95
96
        self._control_files = lockable_files.LockableFiles(
97
            transport.get_transport(self.controldir), 'lock', lockdir.LockDir)
98
        self._format = GitWorkingTreeFormat()
0.200.803 by Jelmer Vernooij
Default to non-bare repositories when initializing a control directory.
99
        self.index = index
0.200.1242 by Jelmer Vernooij
Support directories better.
100
        self._versioned_dirs = None
0.200.239 by Jelmer Vernooij
Provide views.
101
        self.views = self._make_views()
0.200.1173 by Jelmer Vernooij
Provide GitWorkingTree._rules_searcher.
102
        self._rules_searcher = None
0.200.381 by Jelmer Vernooij
Support working trees properly, status and ls.
103
        self._detect_case_handling()
0.200.1202 by Jelmer Vernooij
Implement has_or_had_id.
104
        self._reset_data()
105
        self._fileid_map = self._basis_fileid_map.copy()
0.200.173 by Jelmer Vernooij
Merge changes, open index.
106
0.200.1315 by Jelmer Vernooij
Implement WorkingTree.merge_modified.
107
    def merge_modified(self):
108
        return {}
109
0.200.1220 by Jelmer Vernooij
Support set_parent_trees.
110
    def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
111
        self.set_parent_ids([p for p, t in parents_list])
112
0.264.2 by Jelmer Vernooij
Implement GitWorkingTree.{_add,__iter__,id2path}.
113
    def _index_add_entry(self, path, file_id, kind):
0.200.1206 by Jelmer Vernooij
Implement GitWorkingTree.all_file_ids.
114
        assert isinstance(path, basestring)
0.200.1308 by Jelmer Vernooij
Write index to disk after adding files.
115
        assert type(file_id) == str or file_id is None
0.264.2 by Jelmer Vernooij
Implement GitWorkingTree.{_add,__iter__,id2path}.
116
        if kind == "directory":
117
            # Git indexes don't contain directories
118
            return
119
        if kind == "file":
120
            blob = Blob()
121
            try:
122
                file, stat_val = self.get_file_with_stat(file_id, path)
123
            except (errors.NoSuchFile, IOError):
124
                # TODO: Rather than come up with something here, use the old index
125
                file = StringIO()
126
                from posix import stat_result
127
                stat_val = stat_result((stat.S_IFREG | 0644, 0, 0, 0, 0, 0, 0, 0, 0, 0))
128
            blob.set_raw_string(file.read())
129
        elif kind == "symlink":
130
            blob = Blob()
131
            try:
132
                stat_val = os.lstat(self.abspath(path))
133
            except (errors.NoSuchFile, OSError):
134
                # TODO: Rather than come up with something here, use the 
135
                # old index
136
                from posix import stat_result
137
                stat_val = stat_result((stat.S_IFLNK, 0, 0, 0, 0, 0, 0, 0, 0, 0))
138
            blob.set_raw_string(self.get_symlink_target(file_id).encode("utf-8"))
139
        else:
140
            raise AssertionError("unknown kind '%s'" % kind)
141
        # Add object to the repository if it didn't exist yet
0.200.1205 by Jelmer Vernooij
Implement GitWorkingTree.stored_kind.
142
        if not blob.id in self.store:
143
            self.store.add_object(blob)
0.264.2 by Jelmer Vernooij
Implement GitWorkingTree.{_add,__iter__,id2path}.
144
        # Add an entry to the index or update the existing entry
145
        flags = 0 # FIXME
0.200.1242 by Jelmer Vernooij
Support directories better.
146
        encoded_path = path.encode("utf-8")
147
        self.index[encoded_path] = (stat_val.st_ctime,
0.264.2 by Jelmer Vernooij
Implement GitWorkingTree.{_add,__iter__,id2path}.
148
                stat_val.st_mtime, stat_val.st_dev, stat_val.st_ino,
149
                stat_val.st_mode, stat_val.st_uid, stat_val.st_gid,
150
                stat_val.st_size, blob.id, flags)
0.200.1242 by Jelmer Vernooij
Support directories better.
151
        if self._versioned_dirs is not None:
152
            self._ensure_versioned_dir(encoded_path)
153
154
    def _ensure_versioned_dir(self, dirname):
0.200.1249 by Jelmer Vernooij
Fix file id for tree root
155
        if dirname in self._versioned_dirs:
0.200.1242 by Jelmer Vernooij
Support directories better.
156
            return
0.200.1249 by Jelmer Vernooij
Fix file id for tree root
157
        if dirname != "":
158
            self._ensure_versioned_dir(posixpath.dirname(dirname))
0.200.1242 by Jelmer Vernooij
Support directories better.
159
        self._versioned_dirs.add(dirname)
160
161
    def _load_dirs(self):
162
        self._versioned_dirs = set()
163
        for p in self.index:
164
            self._ensure_versioned_dir(posixpath.dirname(p))
0.264.2 by Jelmer Vernooij
Implement GitWorkingTree.{_add,__iter__,id2path}.
165
0.200.1215 by Jelmer Vernooij
Implement GitWorkingTree.remove.
166
    def _unversion_path(self, path):
167
        encoded_path = path.encode("utf-8")
168
        try:
169
            del self.index[encoded_path]
170
        except KeyError:
171
            # A directory, perhaps?
172
            for p in list(self.index):
173
                if p.startswith(encoded_path+"/"):
174
                    del self.index[p]
0.200.1242 by Jelmer Vernooij
Support directories better.
175
        # FIXME: remove empty directories
0.200.1215 by Jelmer Vernooij
Implement GitWorkingTree.remove.
176
0.200.1192 by Jelmer Vernooij
Implement path2id.
177
    def unversion(self, file_ids):
178
        for file_id in file_ids:
179
            path = self.id2path(file_id)
0.200.1215 by Jelmer Vernooij
Implement GitWorkingTree.remove.
180
            self._unversion_path(path)
181
0.200.1243 by Jelmer Vernooij
Implement WorkingTree.check_state.
182
    def check_state(self):
183
        """Check that the working state is/isn't valid."""
184
        pass
185
0.200.1215 by Jelmer Vernooij
Implement GitWorkingTree.remove.
186
    def remove(self, files, verbose=False, to_file=None, keep_files=True,
187
        force=False):
188
        """Remove nominated files from the working tree metadata.
189
190
        :param files: File paths relative to the basedir.
191
        :param keep_files: If true, the files will also be kept.
192
        :param force: Delete files and directories, even if they are changed
193
            and even if the directories are not empty.
194
        """
195
        all_files = set() # specified and nested files 
196
197
        if isinstance(files, basestring):
198
            files = [files]
199
200
        if to_file is None:
201
            to_file = sys.stdout
202
203
        files = list(all_files)
204
205
        if len(files) == 0:
206
            return # nothing to do
207
208
        # Sort needed to first handle directory content before the directory
209
        files.sort(reverse=True)
210
211
        def backup(file_to_backup):
212
            abs_path = self.abspath(file_to_backup)
213
            backup_name = self.bzrdir._available_backup_name(file_to_backup)
214
            osutils.rename(abs_path, self.abspath(backup_name))
215
            return "removed %s (but kept a copy: %s)" % (
216
                file_to_backup, backup_name)
217
218
        for f in files:
219
            fid = self.path2id(f)
220
            if not fid:
221
                message = "%s is not versioned." % (f,)
222
            else:
223
                abs_path = self.abspath(f)
224
                if verbose:
225
                    # having removed it, it must be either ignored or unknown
226
                    if self.is_ignored(f):
227
                        new_status = 'I'
228
                    else:
229
                        new_status = '?'
230
                    # XXX: Really should be a more abstract reporter interface
231
                    kind_ch = osutils.kind_marker(self.kind(fid))
232
                    to_file.write(new_status + '       ' + f + kind_ch + '\n')
233
                # Unversion file
234
                # FIXME: _unversion_path() is O(size-of-index) for directories
235
                self._unversion_path(f)
236
                message = "removed %s" % (f,)
237
                if osutils.lexists(abs_path):
238
                    if (osutils.isdir(abs_path) and
239
                        len(os.listdir(abs_path)) > 0):
240
                        if force:
241
                            osutils.rmtree(abs_path)
242
                            message = "deleted %s" % (f,)
243
                        else:
244
                            message = backup(f)
245
                    else:
246
                        if not keep_files:
247
                            osutils.delete_any(abs_path)
248
                            message = "deleted %s" % (f,)
249
250
            # print only one message (if any) per file.
251
            if message is not None:
252
                trace.note(message)
0.200.1192 by Jelmer Vernooij
Implement path2id.
253
0.264.2 by Jelmer Vernooij
Implement GitWorkingTree.{_add,__iter__,id2path}.
254
    def _add(self, files, ids, kinds):
255
        for (path, file_id, kind) in zip(files, ids, kinds):
0.200.1201 by Jelmer Vernooij
Implement _set_root_id.
256
            if file_id is not None:
0.200.1210 by Jelmer Vernooij
Implement GitWorkingTree._walkdirs.
257
                self._fileid_map.set_file_id(path.encode("utf-8"), file_id)
0.200.1206 by Jelmer Vernooij
Implement GitWorkingTree.all_file_ids.
258
            else:
0.200.1210 by Jelmer Vernooij
Implement GitWorkingTree._walkdirs.
259
                file_id = self._fileid_map.lookup_file_id(path.encode("utf-8"))
0.200.1206 by Jelmer Vernooij
Implement GitWorkingTree.all_file_ids.
260
            self._index_add_entry(path, file_id, kind)
0.200.1201 by Jelmer Vernooij
Implement _set_root_id.
261
0.200.1308 by Jelmer Vernooij
Write index to disk after adding files.
262
    @needs_tree_write_lock
0.200.1240 by Jelmer Vernooij
Implement GitWorkingTree.smart_add.
263
    def smart_add(self, file_list, recurse=True, action=None, save=True):
264
        added = []
265
        ignored = {}
266
        user_dirs = []
267
        for filepath in osutils.canonical_relpaths(self.basedir, file_list):
0.200.1308 by Jelmer Vernooij
Write index to disk after adding files.
268
            abspath = self.abspath(filepath)
269
            kind = osutils.file_kind(abspath)
0.200.1240 by Jelmer Vernooij
Implement GitWorkingTree.smart_add.
270
            if action is not None:
0.200.1308 by Jelmer Vernooij
Write index to disk after adding files.
271
                file_id = action(self, None, filepath, kind)
0.200.1240 by Jelmer Vernooij
Implement GitWorkingTree.smart_add.
272
            else:
273
                file_id = None
274
            if kind in ("file", "symlink"):
0.200.1308 by Jelmer Vernooij
Write index to disk after adding files.
275
                if save:
0.200.1240 by Jelmer Vernooij
Implement GitWorkingTree.smart_add.
276
                    self._index_add_entry(filepath, file_id, kind)
277
                added.append(filepath)
278
            elif kind == "directory":
279
                if recurse:
280
                    user_dirs.append(filepath)
281
            else:
282
                raise errors.BadFileKindError(filename=abspath, kind=kind)
283
        for user_dir in user_dirs:
284
            abs_user_dir = self.abspath(user_dir)
285
            for name in os.listdir(abs_user_dir):
286
                subp = os.path.join(user_dir, name)
287
                if self.is_control_filename(subp):
0.200.1257 by Jelmer Vernooij
Don't be verbose on control filenames.
288
                    continue
0.200.1240 by Jelmer Vernooij
Implement GitWorkingTree.smart_add.
289
                ignore_glob = self.is_ignored(subp)
290
                if ignore_glob is not None:
291
                    ignored.setdefault(ignore_glob, []).append(subp)
292
                    continue
293
                abspath = self.abspath(subp)
294
                kind = osutils.file_kind(abspath)
295
                if kind == "directory":
296
                    user_dirs.append(subp)
297
                else:
298
                    if action is not None:
299
                        file_id = action()
300
                    else:
301
                        file_id = None
0.200.1308 by Jelmer Vernooij
Write index to disk after adding files.
302
                    if save:
0.200.1240 by Jelmer Vernooij
Implement GitWorkingTree.smart_add.
303
                        self._index_add_entry(subp, file_id, kind)
0.200.1308 by Jelmer Vernooij
Write index to disk after adding files.
304
        if added and save:
305
            self.flush()
0.200.1240 by Jelmer Vernooij
Implement GitWorkingTree.smart_add.
306
        return added, ignored
307
0.200.1201 by Jelmer Vernooij
Implement _set_root_id.
308
    def _set_root_id(self, file_id):
309
        self._fileid_map.set_file_id("", file_id)
0.264.2 by Jelmer Vernooij
Implement GitWorkingTree.{_add,__iter__,id2path}.
310
0.200.1308 by Jelmer Vernooij
Write index to disk after adding files.
311
    @needs_tree_write_lock
0.200.1193 by Jelmer Vernooij
Implement GitWorkingTree.{move,rename_one}.
312
    def move(self, from_paths, to_dir=None, after=False):
313
        rename_tuples = []
314
        to_abs = self.abspath(to_dir)
315
        if not os.path.isdir(to_abs):
316
            raise errors.BzrMoveFailedError('', to_dir,
317
                errors.NotADirectory(to_abs))
318
319
        for from_rel in from_paths:
320
            from_tail = os.path.split(from_rel)[-1]
321
            to_rel = os.path.join(to_dir, from_tail)
322
            self.rename_one(from_rel, to_rel, after=after)
323
            rename_tuples.append((from_rel, to_rel))
324
        return rename_tuples
325
0.200.1308 by Jelmer Vernooij
Write index to disk after adding files.
326
    @needs_tree_write_lock
0.200.1193 by Jelmer Vernooij
Implement GitWorkingTree.{move,rename_one}.
327
    def rename_one(self, from_rel, to_rel, after=False):
328
        if not after:
329
            os.rename(self.abspath(from_rel), self.abspath(to_rel))
0.200.1203 by Jelmer Vernooij
Fix per_workingtree.test_rename_one.TestRenameOne.test_rename_after_non_existant_non_ascii
330
        from_path = from_rel.encode("utf-8")
331
        to_path = to_rel.encode("utf-8")
332
        if not self.has_filename(to_rel):
333
            raise errors.BzrMoveFailedError(from_rel, to_rel,
334
                errors.NoSuchFile(to_rel))
335
        if not from_path in self.index:
336
            raise errors.BzrMoveFailedError(from_rel, to_rel,
337
                errors.NotVersionedError(path=from_rel))
338
        self.index[to_path] = self.index[from_path]
339
        del self.index[from_path]
0.200.1193 by Jelmer Vernooij
Implement GitWorkingTree.{move,rename_one}.
340
0.264.1 by Jelmer Vernooij
Provide stubs using inventory for the moment.:
341
    def get_root_id(self):
0.200.1192 by Jelmer Vernooij
Implement path2id.
342
        return self.path2id("")
343
0.200.1242 by Jelmer Vernooij
Support directories better.
344
    def _has_dir(self, path):
345
        if self._versioned_dirs is None:
346
            self._load_dirs()
347
        return path in self._versioned_dirs
348
0.200.1192 by Jelmer Vernooij
Implement path2id.
349
    @needs_read_lock
350
    def path2id(self, path):
0.200.1242 by Jelmer Vernooij
Support directories better.
351
        encoded_path = path.encode("utf-8")
352
        if self._is_versioned(encoded_path):
353
            return self._fileid_map.lookup_file_id(encoded_path)
354
        return None
0.264.1 by Jelmer Vernooij
Provide stubs using inventory for the moment.:
355
0.200.605 by Jelmer Vernooij
Ignore directories in WorkingTree.extras().
356
    def extras(self):
357
        """Yield all unversioned files in this WorkingTree.
358
        """
0.200.1302 by Jelmer Vernooij
Significantly improve performance of WorkingTree.extras().
359
        present_files = set()
0.200.615 by Jelmer Vernooij
Optimize WorkingTree.extras().
360
        for (dirpath, dirnames, filenames) in os.walk(self.basedir):
0.200.1302 by Jelmer Vernooij
Significantly improve performance of WorkingTree.extras().
361
            dir_relpath = dirpath[len(self.basedir):].strip("/")
362
            if self.bzrdir.is_control_filename(dir_relpath):
0.200.605 by Jelmer Vernooij
Ignore directories in WorkingTree.extras().
363
                continue
0.200.615 by Jelmer Vernooij
Optimize WorkingTree.extras().
364
            for filename in filenames:
0.200.1302 by Jelmer Vernooij
Significantly improve performance of WorkingTree.extras().
365
                relpath = os.path.join(dir_relpath, filename)
366
                present_files.add(relpath)
367
        return present_files - set(self.index)
0.200.605 by Jelmer Vernooij
Ignore directories in WorkingTree.extras().
368
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
369
    def unlock(self):
0.200.224 by Jelmer Vernooij
Fix working tree locking.
370
        # non-implementation specific cleanup
371
        self._cleanup()
372
373
        # reverse order of locking.
374
        try:
375
            return self._control_files.unlock()
376
        finally:
377
            self.branch.unlock()
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
378
0.200.382 by Jelmer Vernooij
Support flushing index.
379
    def flush(self):
380
        # TODO: Maybe this should only write on dirty ?
381
        if self._control_files._lock_mode != 'w':
382
            raise errors.NotWriteLocked(self)
0.200.385 by Jelmer Vernooij
Cope with removed files.
383
        self.index.write()
0.200.382 by Jelmer Vernooij
Support flushing index.
384
0.264.1 by Jelmer Vernooij
Provide stubs using inventory for the moment.:
385
    def __iter__(self):
0.264.2 by Jelmer Vernooij
Implement GitWorkingTree.{_add,__iter__,id2path}.
386
        for path in self.index:
0.200.1192 by Jelmer Vernooij
Implement path2id.
387
            yield self.path2id(path)
0.200.1242 by Jelmer Vernooij
Support directories better.
388
        self._load_dirs()
389
        for path in self._versioned_dirs:
390
            yield self.path2id(path)
0.264.1 by Jelmer Vernooij
Provide stubs using inventory for the moment.:
391
0.200.1202 by Jelmer Vernooij
Implement has_or_had_id.
392
    def has_or_had_id(self, file_id):
393
        if self.has_id(file_id):
394
            return True
395
        if self.had_id(file_id):
396
            return True
397
        return False
398
399
    def had_id(self, file_id):
400
        path = self._basis_fileid_map.lookup_file_id(file_id)
401
        try:
402
            head = self.repository._git.head()
403
        except KeyError:
404
            # Assume no if basis is not accessible
405
            return False
0.200.1205 by Jelmer Vernooij
Implement GitWorkingTree.stored_kind.
406
        if head == ZERO_SHA:
407
            return False
0.200.1202 by Jelmer Vernooij
Implement has_or_had_id.
408
        root_tree = self.store[head].tree
409
        try:
410
            tree_lookup_path(self.store.__getitem__, root_tree, path)
411
        except KeyError:
412
            return False
413
        else:
414
            return True
415
0.200.1198 by Jelmer Vernooij
Implement GitWorkingTree.has_id.
416
    def has_id(self, file_id):
417
        try:
418
            self.id2path(file_id)
419
        except errors.NoSuchId:
420
            return False
421
        else:
422
            return True
423
0.264.1 by Jelmer Vernooij
Provide stubs using inventory for the moment.:
424
    def id2path(self, file_id):
0.264.2 by Jelmer Vernooij
Implement GitWorkingTree.{_add,__iter__,id2path}.
425
        if type(file_id) != str:
426
            raise AssertionError
427
        path = self._fileid_map.lookup_path(file_id)
0.200.1213 by Jelmer Vernooij
Add note about directories.
428
        # FIXME: What about directories?
0.200.1242 by Jelmer Vernooij
Support directories better.
429
        if self._is_versioned(path):
430
            return path.decode("utf-8")
0.200.1214 by Jelmer Vernooij
Set container when raising NoSuchId.
431
        raise errors.NoSuchId(self, file_id)
0.264.1 by Jelmer Vernooij
Provide stubs using inventory for the moment.:
432
0.200.1200 by Jelmer Vernooij
Support GitWorkingTree.get_file_mtime.
433
    def get_file_mtime(self, file_id, path=None):
434
        """See Tree.get_file_mtime."""
435
        if not path:
436
            path = self.id2path(file_id)
437
        return os.lstat(self.abspath(path)).st_mtime
438
0.200.409 by Jelmer Vernooij
Support parsing .gitignore.
439
    def get_ignore_list(self):
440
        ignoreset = getattr(self, '_ignoreset', None)
441
        if ignoreset is not None:
442
            return ignoreset
443
444
        ignore_globs = set()
445
        ignore_globs.update(ignores.get_runtime_ignores())
446
        ignore_globs.update(ignores.get_user_ignores())
447
        if self.has_filename(IGNORE_FILENAME):
448
            f = self.get_file_byname(IGNORE_FILENAME)
449
            try:
0.200.1198 by Jelmer Vernooij
Implement GitWorkingTree.has_id.
450
                # FIXME: Parse git file format, rather than assuming it's
451
                # the same as for bzr's native formats.
0.200.409 by Jelmer Vernooij
Support parsing .gitignore.
452
                ignore_globs.update(ignores.parse_ignore_file(f))
453
            finally:
454
                f.close()
455
        self._ignoreset = ignore_globs
456
        return ignore_globs
457
0.200.508 by Jelmer Vernooij
Skip inventory caching bits.
458
    def set_last_revision(self, revid):
459
        self._change_last_revision(revid)
460
0.200.379 by Jelmer Vernooij
Re-enable working tree support.
461
    def _reset_data(self):
0.248.3 by Jelmer Vernooij
Handle working trees without valid HEAD branch.
462
        try:
463
            head = self.repository._git.head()
464
        except KeyError, name:
0.200.1308 by Jelmer Vernooij
Write index to disk after adding files.
465
            raise errors.NotBranchError("branch %s at %s" % (name,
466
                self.repository.base))
0.200.948 by Jelmer Vernooij
Cope with empty inventories.
467
        if head == ZERO_SHA:
0.200.1202 by Jelmer Vernooij
Implement has_or_had_id.
468
            self._basis_fileid_map = GitFileIdMap({}, self.mapping)
0.200.948 by Jelmer Vernooij
Cope with empty inventories.
469
        else:
0.200.1308 by Jelmer Vernooij
Write index to disk after adding files.
470
            self._basis_fileid_map = self.mapping.get_fileid_map(
471
                self.store.__getitem__, self.store[head].tree)
0.200.379 by Jelmer Vernooij
Re-enable working tree support.
472
0.200.381 by Jelmer Vernooij
Support working trees properly, status and ls.
473
    @needs_read_lock
0.200.1302 by Jelmer Vernooij
Significantly improve performance of WorkingTree.extras().
474
    def get_file_verifier(self, file_id, path=None, stat_value=None):
475
        if path is None:
476
            path = self.id2path(file_id)
477
        return ("GIT", self.index[path][-2])
478
479
    @needs_read_lock
0.200.381 by Jelmer Vernooij
Support working trees properly, status and ls.
480
    def get_file_sha1(self, file_id, path=None, stat_value=None):
481
        if not path:
0.264.2 by Jelmer Vernooij
Implement GitWorkingTree.{_add,__iter__,id2path}.
482
            path = self.id2path(file_id)
0.200.1302 by Jelmer Vernooij
Significantly improve performance of WorkingTree.extras().
483
        abspath = self.abspath(path).encode(osutils._fs_enc)
0.239.4 by Jelmer Vernooij
Cope with nonexistent files and directories in get_file_sha1.
484
        try:
0.200.1302 by Jelmer Vernooij
Significantly improve performance of WorkingTree.extras().
485
            return osutils.sha_file_by_name(abspath)
0.239.4 by Jelmer Vernooij
Cope with nonexistent files and directories in get_file_sha1.
486
        except OSError, (num, msg):
487
            if num in (errno.EISDIR, errno.ENOENT):
488
                return None
489
            raise
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
490
0.200.610 by Jelmer Vernooij
Support retrieving basis tree properly.
491
    def revision_tree(self, revid):
492
        return self.repository.revision_tree(revid)
493
0.200.1242 by Jelmer Vernooij
Support directories better.
494
    def _is_versioned(self, path):
495
        return (path in self.index or self._has_dir(path))
496
0.200.1239 by Jelmer Vernooij
Implement GitWorkingTree.filter_unversioned_files.
497
    def filter_unversioned_files(self, files):
0.200.1316 by Jelmer Vernooij
Fix filter_unversioned_files.
498
        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.
499
0.264.11 by Jelmer Vernooij
Completer implementation of iter_entries_by_dir and list_files.
500
    def _get_dir_ie(self, path, parent_id):
0.200.1192 by Jelmer Vernooij
Implement path2id.
501
        file_id = self.path2id(path)
0.264.11 by Jelmer Vernooij
Completer implementation of iter_entries_by_dir and list_files.
502
        return inventory.InventoryDirectory(file_id,
0.200.1190 by Jelmer Vernooij
Fix get_symlink_target call.
503
            posixpath.basename(path).strip("/"), parent_id)
0.264.11 by Jelmer Vernooij
Completer implementation of iter_entries_by_dir and list_files.
504
505
    def _add_missing_parent_ids(self, path, dir_ids):
506
        if path in dir_ids:
507
            return []
508
        parent = posixpath.dirname(path).strip("/")
509
        ret = self._add_missing_parent_ids(parent, dir_ids)
510
        parent_id = dir_ids[parent]
511
        ie = self._get_dir_ie(path, parent_id)
512
        dir_ids[path] = ie.file_id
513
        ret.append((path, ie))
514
        return ret
515
0.264.10 by Jelmer Vernooij
Yield inventory entries.
516
    def _get_file_ie(self, path, value, parent_id):
0.200.1192 by Jelmer Vernooij
Implement path2id.
517
        assert isinstance(path, unicode)
0.264.10 by Jelmer Vernooij
Yield inventory entries.
518
        assert isinstance(value, tuple) and len(value) == 10
519
        (ctime, mtime, dev, ino, mode, uid, gid, size, sha, flags) = value
0.200.1192 by Jelmer Vernooij
Implement path2id.
520
        file_id = self.path2id(path)
0.264.10 by Jelmer Vernooij
Yield inventory entries.
521
        if type(file_id) != str:
522
            raise AssertionError
523
        kind = mode_kind(mode)
524
        ie = inventory.entry_factory[kind](file_id,
0.200.1192 by Jelmer Vernooij
Implement path2id.
525
            posixpath.basename(path), parent_id)
0.264.10 by Jelmer Vernooij
Yield inventory entries.
526
        if kind == 'symlink':
0.200.1190 by Jelmer Vernooij
Fix get_symlink_target call.
527
            ie.symlink_target = self.get_symlink_target(file_id)
0.264.10 by Jelmer Vernooij
Yield inventory entries.
528
        else:
529
            data = self.get_file_text(file_id, path)
530
            ie.text_sha1 = osutils.sha_string(data)
531
            ie.text_size = len(data)
532
            ie.executable = self.is_executable(file_id, path)
533
        ie.revision = None
534
        return ie
535
0.264.11 by Jelmer Vernooij
Completer implementation of iter_entries_by_dir and list_files.
536
    def _is_executable_from_path_and_stat_from_stat(self, path, stat_result):
537
        mode = stat_result.st_mode
538
        return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
539
0.200.1205 by Jelmer Vernooij
Implement GitWorkingTree.stored_kind.
540
    def stored_kind(self, file_id, path=None):
541
        if path is None:
542
            path = self.id2path(file_id)
543
        head = self.repository._git.head()
544
        if head == ZERO_SHA:
545
            raise errors.NoSuchId(self, file_id)
546
        root_tree = self.store[head].tree
547
        (mode, hexsha) = tree_lookup_path(self.store.__getitem__, root_tree, path)
548
        return mode_kind(mode)
549
0.264.11 by Jelmer Vernooij
Completer implementation of iter_entries_by_dir and list_files.
550
    if not osutils.supports_executable():
551
        def is_executable(self, file_id, path=None):
552
            basis_tree = self.basis_tree()
553
            if file_id in basis_tree:
554
                return basis_tree.is_executable(file_id)
555
            # Default to not executable
556
            return False
557
    else:
558
        def is_executable(self, file_id, path=None):
559
            if not path:
560
                path = self.id2path(file_id)
561
            mode = os.lstat(self.abspath(path)).st_mode
562
            return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
563
564
        _is_executable_from_path_and_stat = \
565
            _is_executable_from_path_and_stat_from_stat
566
0.264.10 by Jelmer Vernooij
Yield inventory entries.
567
    def list_files(self, include_root=False, from_dir=None, recursive=True):
0.264.11 by Jelmer Vernooij
Completer implementation of iter_entries_by_dir and list_files.
568
        # FIXME: Yield non-versioned files
569
        # FIXME: support from_dir
570
        # FIXME: Support recursive
571
        dir_ids = {}
0.200.1192 by Jelmer Vernooij
Implement path2id.
572
        root_ie = self._get_dir_ie(u"", None)
0.264.11 by Jelmer Vernooij
Completer implementation of iter_entries_by_dir and list_files.
573
        if include_root and not from_dir:
574
            yield "", "V", root_ie.kind, root_ie.file_id, root_ie
0.200.1192 by Jelmer Vernooij
Implement path2id.
575
        dir_ids[u""] = root_ie.file_id
0.264.11 by Jelmer Vernooij
Completer implementation of iter_entries_by_dir and list_files.
576
        for path, value in self.index.iteritems():
0.200.1192 by Jelmer Vernooij
Implement path2id.
577
            path = path.decode("utf-8")
0.264.11 by Jelmer Vernooij
Completer implementation of iter_entries_by_dir and list_files.
578
            parent = posixpath.dirname(path).strip("/")
579
            for dir_path, dir_ie in self._add_missing_parent_ids(parent, dir_ids):
580
                yield dir_path, "V", dir_ie.kind, dir_ie.file_id, dir_ie
581
            ie = self._get_file_ie(path, value, dir_ids[parent])
582
            yield path, "V", ie.kind, ie.file_id, ie
0.264.10 by Jelmer Vernooij
Yield inventory entries.
583
0.200.1206 by Jelmer Vernooij
Implement GitWorkingTree.all_file_ids.
584
    def all_file_ids(self):
585
        ids = {u"": self.path2id("")}
586
        for path in self.index:
587
            path = path.decode("utf-8")
588
            parent = posixpath.dirname(path).strip("/")
589
            for e in self._add_missing_parent_ids(parent, ids):
590
                pass
591
            ids[path] = self.path2id(path)
592
        return set(ids.values())
593
0.264.9 by Jelmer Vernooij
Implement basic GitWorkingTree.iter_entries_by_dir.
594
    def iter_entries_by_dir(self, specific_file_ids=None, yield_parents=False):
595
        # FIXME: Is return order correct?
0.200.1252 by Jelmer Vernooij
Support specific_file_ids in GitWorkingTree.iter_entries_by_dir.
596
        if yield_parents:
597
            raise NotImplementedError(self.iter_entries_by_dir)
0.264.9 by Jelmer Vernooij
Implement basic GitWorkingTree.iter_entries_by_dir.
598
        if specific_file_ids is not None:
0.200.1252 by Jelmer Vernooij
Support specific_file_ids in GitWorkingTree.iter_entries_by_dir.
599
            specific_paths = [self.id2path(file_id) for file_id in specific_file_ids]
600
            if specific_paths in ([u""], []):
601
                specific_paths = None
602
            else:
603
                specific_paths = set(specific_paths)
604
        else:
605
            specific_paths = None
0.200.1192 by Jelmer Vernooij
Implement path2id.
606
        root_ie = self._get_dir_ie(u"", None)
0.200.1252 by Jelmer Vernooij
Support specific_file_ids in GitWorkingTree.iter_entries_by_dir.
607
        if specific_paths is None:
608
            yield u"", root_ie
0.200.1192 by Jelmer Vernooij
Implement path2id.
609
        dir_ids = {u"": root_ie.file_id}
0.264.10 by Jelmer Vernooij
Yield inventory entries.
610
        for path, value in self.index.iteritems():
0.200.1192 by Jelmer Vernooij
Implement path2id.
611
            path = path.decode("utf-8")
0.200.1252 by Jelmer Vernooij
Support specific_file_ids in GitWorkingTree.iter_entries_by_dir.
612
            if specific_paths is not None and not path in specific_paths:
613
                continue
0.200.1251 by Jelmer Vernooij
Cope with files dissapearing in GitWorkingTree.
614
            try:
615
                file_ie = self._get_file_ie(path, value, None)
616
            except IOError:
617
                continue
0.264.11 by Jelmer Vernooij
Completer implementation of iter_entries_by_dir and list_files.
618
            parent = posixpath.dirname(path).strip("/")
0.200.1207 by Jelmer Vernooij
Fix some formatting.
619
            for (dir_path, dir_ie) in self._add_missing_parent_ids(parent,
620
                    dir_ids):
0.264.11 by Jelmer Vernooij
Completer implementation of iter_entries_by_dir and list_files.
621
                yield dir_path, dir_ie
0.200.1251 by Jelmer Vernooij
Cope with files dissapearing in GitWorkingTree.
622
            file_ie.parent_id = self.path2id(parent)
623
            yield path, file_ie
0.264.9 by Jelmer Vernooij
Implement basic GitWorkingTree.iter_entries_by_dir.
624
0.200.619 by Jelmer Vernooij
Provide dummy WorkingTree.conflicts() implementation rather than spending a lot of time not finding any conflicts.
625
    @needs_read_lock
626
    def conflicts(self):
627
        # FIXME:
0.262.1 by Jelmer Vernooij
Fix WorkingTree.conflicts().
628
        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.
629
0.200.1193 by Jelmer Vernooij
Implement GitWorkingTree.{move,rename_one}.
630
    def update_basis_by_delta(self, new_revid, delta):
0.200.1196 by Jelmer Vernooij
Implement GitWorkingTree.update_basis_by_delta.
631
        # The index just contains content, which won't have changed.
0.200.1202 by Jelmer Vernooij
Implement has_or_had_id.
632
        self._reset_data()
0.200.1193 by Jelmer Vernooij
Implement GitWorkingTree.{move,rename_one}.
633
0.200.1210 by Jelmer Vernooij
Implement GitWorkingTree._walkdirs.
634
    def _walkdirs(self, prefix=""):
635
        if prefix != "":
636
            prefix += "/"
637
        per_dir = defaultdict(list)
638
        for path, value in self.index.iteritems():
639
            if not path.startswith(prefix):
640
                continue
641
            (dirname, child_name) = posixpath.split(path)
642
            dirname = dirname.decode("utf-8")
643
            dir_file_id = self.path2id(dirname)
644
            assert isinstance(value, tuple) and len(value) == 10
645
            (ctime, mtime, dev, ino, mode, uid, gid, size, sha, flags) = value
0.200.1211 by Jelmer Vernooij
Fix statresult.
646
            stat_result = posix.stat_result((mode, ino,
647
                    dev, 1, uid, gid, size,
648
                    0, mtime, ctime))
0.200.1210 by Jelmer Vernooij
Implement GitWorkingTree._walkdirs.
649
            per_dir[(dirname, dir_file_id)].append(
650
                (path.decode("utf-8"), child_name.decode("utf-8"),
651
                mode_kind(mode), stat_result,
652
                self.path2id(path.decode("utf-8")),
653
                mode_kind(mode)))
654
        return per_dir.iteritems()
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
655
0.200.1308 by Jelmer Vernooij
Write index to disk after adding files.
656
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
657
class GitWorkingTreeFormat(workingtree.WorkingTreeFormat):
658
0.200.1206 by Jelmer Vernooij
Implement GitWorkingTree.all_file_ids.
659
    _tree_class = GitWorkingTree
660
0.200.1295 by Jelmer Vernooij
Mark working trees as not supporting directories.
661
    supports_versioned_directories = False
662
0.200.656 by Jelmer Vernooij
Implement GitWorkingTreeFormat._matchingbzrdir.
663
    @property
664
    def _matchingbzrdir(self):
0.200.1140 by Jelmer Vernooij
Update now that the control dir formats are no longer in __init__.
665
        from bzrlib.plugins.git.dir import LocalGitControlDirFormat
0.200.1013 by Jelmer Vernooij
More renames.
666
        return LocalGitControlDirFormat()
0.200.656 by Jelmer Vernooij
Implement GitWorkingTreeFormat._matchingbzrdir.
667
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
668
    def get_format_description(self):
669
        return "Git Working Tree"
0.200.616 by Jelmer Vernooij
Provide custom intertree implementation for GitRevisionTree->GitWorkingTree.
670
0.200.1096 by Jelmer Vernooij
Implement GitWorkingTreeFormat.initialize.
671
    def initialize(self, a_bzrdir, revision_id=None, from_branch=None,
672
                   accelerator_tree=None, hardlink=False):
673
        """See WorkingTreeFormat.initialize()."""
674
        if not isinstance(a_bzrdir, LocalGitDir):
675
            raise errors.IncompatibleFormat(self, a_bzrdir)
676
        index = Index(a_bzrdir.root_transport.local_abspath(".git/index"))
677
        index.write()
678
        return GitWorkingTree(a_bzrdir, a_bzrdir.open_repository(),
679
            a_bzrdir.open_branch(), index)
680
0.200.616 by Jelmer Vernooij
Provide custom intertree implementation for GitRevisionTree->GitWorkingTree.
681
682
class InterIndexGitTree(tree.InterTree):
683
    """InterTree that works between a Git revision tree and an index."""
684
685
    def __init__(self, source, target):
686
        super(InterIndexGitTree, self).__init__(source, target)
687
        self._index = target.index
688
689
    @classmethod
690
    def is_compatible(cls, source, target):
691
        from bzrlib.plugins.git.repository import GitRevisionTree
692
        return (isinstance(source, GitRevisionTree) and 
693
                isinstance(target, GitWorkingTree))
694
695
    def compare(self, want_unchanged=False, specific_files=None,
696
                extra_trees=None, require_versioned=False, include_root=False,
697
                want_unversioned=False):
698
        changes = self._index.changes_from_tree(
0.200.1205 by Jelmer Vernooij
Implement GitWorkingTree.stored_kind.
699
            self.source.store, self.source.tree, 
0.200.616 by Jelmer Vernooij
Provide custom intertree implementation for GitRevisionTree->GitWorkingTree.
700
            want_unchanged=want_unchanged)
0.200.920 by Jelmer Vernooij
Fix some more tests.
701
        source_fileid_map = self.source.mapping.get_fileid_map(
0.200.1205 by Jelmer Vernooij
Implement GitWorkingTree.stored_kind.
702
            self.source.store.__getitem__,
0.252.43 by Jelmer Vernooij
Some refactoring, support proper file ids in revision deltas.
703
            self.source.tree)
0.200.922 by Jelmer Vernooij
Fix remaining test.
704
        if self.target.mapping.BZR_FILE_IDS_FILE is not None:
0.200.1030 by Jelmer Vernooij
More work on supporting roundtripping push.
705
            file_id = self.target.path2id(
706
                self.target.mapping.BZR_FILE_IDS_FILE)
707
            if file_id is None:
0.200.922 by Jelmer Vernooij
Fix remaining test.
708
                target_fileid_map = {}
709
            else:
0.200.1207 by Jelmer Vernooij
Fix some formatting.
710
                target_fileid_map = self.target.mapping.import_fileid_map(
711
                    Blob.from_string(self.target.get_file_text(file_id)))
0.200.922 by Jelmer Vernooij
Fix remaining test.
712
        else:
713
            target_fileid_map = {}
0.200.1207 by Jelmer Vernooij
Fix some formatting.
714
        target_fileid_map = GitFileIdMap(target_fileid_map,
715
                self.target.mapping)
0.200.1030 by Jelmer Vernooij
More work on supporting roundtripping push.
716
        ret = tree_delta_from_git_changes(changes, self.target.mapping,
0.252.43 by Jelmer Vernooij
Some refactoring, support proper file ids in revision deltas.
717
            (source_fileid_map, target_fileid_map),
0.200.616 by Jelmer Vernooij
Provide custom intertree implementation for GitRevisionTree->GitWorkingTree.
718
            specific_file=specific_files, require_versioned=require_versioned)
719
        if want_unversioned:
720
            for e in self.target.extras():
0.200.1207 by Jelmer Vernooij
Fix some formatting.
721
                ret.unversioned.append((e, None,
722
                    osutils.file_kind(self.target.abspath(e))))
0.200.616 by Jelmer Vernooij
Provide custom intertree implementation for GitRevisionTree->GitWorkingTree.
723
        return ret
724
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
725
    def iter_changes(self, include_unchanged=False, specific_files=None,
0.200.1207 by Jelmer Vernooij
Fix some formatting.
726
        pb=None, extra_trees=[], require_versioned=True,
727
        want_unversioned=False):
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
728
        changes = self._index.changes_from_tree(
0.200.1205 by Jelmer Vernooij
Implement GitWorkingTree.stored_kind.
729
            self.source.store, self.source.tree,
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
730
            want_unchanged=include_unchanged)
731
        # FIXME: Handle want_unversioned
0.200.1179 by Jelmer Vernooij
Avoid using verifiers for natively imported revisions, save a lot of time.
732
        return changes_from_git_changes(changes, self.target.mapping,
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
733
            specific_file=specific_files)
0.200.616 by Jelmer Vernooij
Provide custom intertree implementation for GitRevisionTree->GitWorkingTree.
734
0.200.1179 by Jelmer Vernooij
Avoid using verifiers for natively imported revisions, save a lot of time.
735
0.200.616 by Jelmer Vernooij
Provide custom intertree implementation for GitRevisionTree->GitWorkingTree.
736
tree.InterTree.register_optimiser(InterIndexGitTree)