/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
0.358.2 by Jelmer Vernooij
Refresh copyright headers, add my email.
1
# Copyright (C) 2008-2018 Jelmer Vernooij <jelmer@jelmer.uk>
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
0.358.1 by Jelmer Vernooij
Fix FSF address.
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
16
0.200.381 by Jelmer Vernooij
Support working trees properly, status and ls.
17
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
18
"""An adapter between a Git index and a Bazaar Working Tree"""
19
0.200.1594 by Jelmer Vernooij
Use absolute_import everywhere.
20
from __future__ import absolute_import
0.200.381 by Jelmer Vernooij
Support working trees properly, status and ls.
21
0.200.1731 by Jelmer Vernooij
Add support for checking untracked changes.
22
import itertools
0.200.385 by Jelmer Vernooij
Cope with removed files.
23
from cStringIO import (
24
    StringIO,
25
    )
0.200.1210 by Jelmer Vernooij
Implement GitWorkingTree._walkdirs.
26
from collections import defaultdict
0.239.4 by Jelmer Vernooij
Cope with nonexistent files and directories in get_file_sha1.
27
import errno
0.200.1538 by Jelmer Vernooij
More work on tree-reference support.
28
from dulwich.errors import NotGitRepository
0.200.1655 by Jelmer Vernooij
Basic support for git ignores.
29
from dulwich.ignore import (
0.200.1658 by Jelmer Vernooij
Fix handling of ignores - return patterns that matched.
30
    IgnoreFilterManager,
0.200.1655 by Jelmer Vernooij
Basic support for git ignores.
31
    )
0.200.1096 by Jelmer Vernooij
Implement GitWorkingTreeFormat.initialize.
32
from dulwich.index import (
33
    Index,
0.371.1 by Jelmer Vernooij
Implement WorkingTree.reset_state().
34
    build_index_from_tree,
0.200.1531 by Jelmer Vernooij
Don't trust index contents - verify against file timestamps.
35
    changes_from_tree,
36
    cleanup_mode,
0.200.1754 by Jelmer Vernooij
Avoid updating index when just comparing trees.
37
    commit_tree,
0.391.6 by Jelmer Vernooij
Directly call index_entry_from_path.
38
    index_entry_from_path,
0.200.1531 by Jelmer Vernooij
Don't trust index contents - verify against file timestamps.
39
    index_entry_from_stat,
0.391.5 by Jelmer Vernooij
Use iter_fresh_entries directly.
40
    iter_fresh_entries,
0.323.1 by Jelmer Vernooij
Fix iter_changes of untracked changes.
41
    blob_from_path_and_stat,
0.369.1 by Jelmer Vernooij
Implement conflict handling.
42
    FLAG_STAGEMASK,
0.373.1 by Jelmer Vernooij
Fix WorkingTree.reset_state().
43
    validate_path,
0.200.1096 by Jelmer Vernooij
Implement GitWorkingTreeFormat.initialize.
44
    )
0.200.1202 by Jelmer Vernooij
Implement has_or_had_id.
45
from dulwich.object_store import (
46
    tree_lookup_path,
47
    )
0.200.383 by Jelmer Vernooij
Simplify, support rewriting index based on inventory.
48
from dulwich.objects import (
49
    Blob,
0.357.1 by Jelmer Vernooij
Fix some remove tests.
50
    Tree,
0.200.1538 by Jelmer Vernooij
More work on tree-reference support.
51
    S_IFGITLINK,
0.373.1 by Jelmer Vernooij
Fix WorkingTree.reset_state().
52
    S_ISGITLINK,
0.391.7 by Jelmer Vernooij
Fix reporting of missing files in .iter_changes.
53
    ZERO_SHA,
0.200.948 by Jelmer Vernooij
Cope with empty inventories.
54
    )
0.200.1538 by Jelmer Vernooij
More work on tree-reference support.
55
from dulwich.repo import Repo
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
56
import os
0.264.10 by Jelmer Vernooij
Yield inventory entries.
57
import posixpath
0.200.1655 by Jelmer Vernooij
Basic support for git ignores.
58
import re
0.200.384 by Jelmer Vernooij
Fix reading of inventory from index.
59
import stat
0.200.1215 by Jelmer Vernooij
Implement GitWorkingTree.remove.
60
import sys
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
61
0.200.1641 by Jelmer Vernooij
Use relative imports where possible.
62
from ... import (
0.369.1 by Jelmer Vernooij
Implement conflict handling.
63
    conflicts as _mod_conflicts,
0.200.382 by Jelmer Vernooij
Support flushing index.
64
    errors,
0.200.1752 by Jelmer Vernooij
Don't traverse nested trees in WorkingTree.smart_add.
65
    controldir as _mod_controldir,
0.200.1655 by Jelmer Vernooij
Basic support for git ignores.
66
    globbing,
0.200.409 by Jelmer Vernooij
Support parsing .gitignore.
67
    ignores,
0.200.1476 by Jelmer Vernooij
Cope with working tree refactoring.
68
    lock,
0.380.1 by Jelmer Vernooij
Implement WorkingTree.pull.
69
    merge,
0.200.381 by Jelmer Vernooij
Support working trees properly, status and ls.
70
    osutils,
0.200.1720 by Jelmer Vernooij
Fix handling of pending merges.
71
    revision as _mod_revision,
0.200.1215 by Jelmer Vernooij
Implement GitWorkingTree.remove.
72
    trace,
0.200.1752 by Jelmer Vernooij
Don't traverse nested trees in WorkingTree.smart_add.
73
    transport as _mod_transport,
0.200.519 by Jelmer Vernooij
Move imports down, might not be available in older bzr-git versions.
74
    tree,
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
75
    workingtree,
76
    )
0.383.1 by Jelmer Vernooij
Fix a bunch of locking issues.
77
from ...decorators import (
78
    only_raises,
79
    )
0.200.1648 by Jelmer Vernooij
Fix compatibility with newer versions of breezy.
80
from ...bzr import (
81
    inventory,
82
    )
0.200.1680 by Jelmer Vernooij
Fix repo locks.
83
from ...mutabletree import (
84
    MutableTree,
85
    )
0.200.1641 by Jelmer Vernooij
Use relative imports where possible.
86
87
88
from .dir import (
0.200.1096 by Jelmer Vernooij
Implement GitWorkingTreeFormat.initialize.
89
    LocalGitDir,
90
    )
0.200.1641 by Jelmer Vernooij
Use relative imports where possible.
91
from .tree import (
0.200.622 by Jelmer Vernooij
Implement InterTree.iter_changes() as well.
92
    changes_from_git_changes,
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
93
    tree_delta_from_git_changes,
0.287.3 by Jelmer Vernooij
Some improvements to changes iterator.
94
    InterGitTrees,
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
95
    MutableGitIndexTree,
0.200.617 by Jelmer Vernooij
Add custom InterTree for use between git revision trees.
96
    )
0.200.1641 by Jelmer Vernooij
Use relative imports where possible.
97
from .mapping import (
0.200.971 by Chadrik
Fix 'bzr status' after 'bzr add' in native git working trees.
98
    GitFileIdMap,
0.264.10 by Jelmer Vernooij
Yield inventory entries.
99
    mode_kind,
0.200.971 by Chadrik
Fix 'bzr status' after 'bzr add' in native git working trees.
100
    )
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
101
0.200.409 by Jelmer Vernooij
Support parsing .gitignore.
102
IGNORE_FILENAME = ".gitignore"
103
104
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
105
class GitWorkingTree(MutableGitIndexTree,workingtree.WorkingTree):
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
106
    """A Git working tree."""
107
0.200.1648 by Jelmer Vernooij
Fix compatibility with newer versions of breezy.
108
    def __init__(self, controldir, repo, branch, index):
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
109
        MutableGitIndexTree.__init__(self)
0.200.1741 by Jelmer Vernooij
Fix opentree tests.
110
        basedir = controldir.root_transport.local_abspath('.')
111
        self.basedir = osutils.realpath(basedir)
0.200.1648 by Jelmer Vernooij
Fix compatibility with newer versions of breezy.
112
        self.controldir = controldir
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
113
        self.repository = repo
0.200.1205 by Jelmer Vernooij
Implement GitWorkingTree.stored_kind.
114
        self.store = self.repository._git.object_store
0.200.384 by Jelmer Vernooij
Fix reading of inventory from index.
115
        self.mapping = self.repository.get_mapping()
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
116
        self._branch = branch
0.200.1648 by Jelmer Vernooij
Fix compatibility with newer versions of breezy.
117
        self._transport = controldir.transport
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
118
        self._format = GitWorkingTreeFormat()
0.200.803 by Jelmer Vernooij
Default to non-bare repositories when initializing a control directory.
119
        self.index = index
0.200.239 by Jelmer Vernooij
Provide views.
120
        self.views = self._make_views()
0.200.1173 by Jelmer Vernooij
Provide GitWorkingTree._rules_searcher.
121
        self._rules_searcher = None
0.200.381 by Jelmer Vernooij
Support working trees properly, status and ls.
122
        self._detect_case_handling()
0.200.1202 by Jelmer Vernooij
Implement has_or_had_id.
123
        self._reset_data()
0.200.1476 by Jelmer Vernooij
Cope with working tree refactoring.
124
0.200.1650 by Jelmer Vernooij
Implement GitWorkingTree.supports_tree_reference.
125
    def supports_tree_reference(self):
126
        return False
127
0.349.1 by Jelmer Vernooij
Support supports_rename_tracking method.
128
    def supports_rename_tracking(self):
129
        return False
130
0.200.1476 by Jelmer Vernooij
Cope with working tree refactoring.
131
    def lock_read(self):
132
        """Lock the repository for read operations.
133
0.200.1646 by Jelmer Vernooij
Rename bzrlib to breezy.
134
        :return: A breezy.lock.LogicalLockResult.
0.200.1476 by Jelmer Vernooij
Cope with working tree refactoring.
135
        """
136
        if not self._lock_mode:
137
            self._lock_mode = 'r'
138
            self._lock_count = 1
0.200.1525 by Jelmer Vernooij
Make sure to always use an up-to-date index.
139
            self.index.read()
0.200.1476 by Jelmer Vernooij
Cope with working tree refactoring.
140
        else:
141
            self._lock_count += 1
142
        self.branch.lock_read()
143
        return lock.LogicalLockResult(self.unlock)
144
0.383.1 by Jelmer Vernooij
Fix a bunch of locking issues.
145
    def _lock_write_tree(self):
146
        # TODO(jelmer): Actually create index.lock
147
        if not self._lock_mode:
148
            self._lock_mode = 'w'
149
            self._lock_count = 1
150
            self.index.read()
151
        elif self._lock_mode == 'r':
152
            raise errors.ReadOnlyError(self)
153
        else:
154
            self._lock_count +=1
155
0.200.1477 by Jelmer Vernooij
Implement GitWorkingTree.lock_tree_write.
156
    def lock_tree_write(self):
157
        self.branch.lock_read()
0.383.1 by Jelmer Vernooij
Fix a bunch of locking issues.
158
        try:
159
            self._lock_write_tree()
160
            return lock.LogicalLockResult(self.unlock)
161
        except:
162
            self.branch.unlock()
163
            raise
0.200.1477 by Jelmer Vernooij
Implement GitWorkingTree.lock_tree_write.
164
0.200.1476 by Jelmer Vernooij
Cope with working tree refactoring.
165
    def lock_write(self, token=None):
166
        self.branch.lock_write()
0.383.1 by Jelmer Vernooij
Fix a bunch of locking issues.
167
        try:
168
            self._lock_write_tree()
169
            return lock.LogicalLockResult(self.unlock)
170
        except:
171
            self.branch.unlock()
172
            raise
0.200.1476 by Jelmer Vernooij
Cope with working tree refactoring.
173
174
    def is_locked(self):
175
        return self._lock_count >= 1
176
177
    def get_physical_lock_status(self):
178
        return False
179
0.383.1 by Jelmer Vernooij
Fix a bunch of locking issues.
180
    @only_raises(errors.LockNotHeld, errors.LockBroken)
0.200.1476 by Jelmer Vernooij
Cope with working tree refactoring.
181
    def unlock(self):
182
        if not self._lock_count:
183
            return lock.cant_unlock_not_held(self)
0.383.1 by Jelmer Vernooij
Fix a bunch of locking issues.
184
        try:
185
            self._cleanup()
186
            self._lock_count -= 1
187
            if self._lock_count > 0:
188
                return
189
            self._lock_mode = None
190
        finally:
191
            self.branch.unlock()
0.200.1658 by Jelmer Vernooij
Fix handling of ignores - return patterns that matched.
192
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
193
    def _cleanup(self):
194
        pass
195
0.200.1322 by Jelmer Vernooij
Add case detection.
196
    def _detect_case_handling(self):
197
        try:
198
            self._transport.stat(".git/cOnFiG")
199
        except errors.NoSuchFile:
200
            self.case_sensitive = True
201
        else:
202
            self.case_sensitive = False
203
0.200.1315 by Jelmer Vernooij
Implement WorkingTree.merge_modified.
204
    def merge_modified(self):
205
        return {}
206
0.200.1696 by Jelmer Vernooij
Fix set_merge_modified.
207
    def set_merge_modified(self, modified_hashes):
0.298.1 by Jelmer Vernooij
mark GitWorkngTree.set_merge_modified as unsupported.
208
        raise errors.UnsupportedOperation(self.set_merge_modified, self)
0.200.1690 by Jelmer Vernooij
Implement WorkingTree.set_merge_modified.
209
0.200.1220 by Jelmer Vernooij
Support set_parent_trees.
210
    def set_parent_trees(self, parents_list, allow_leftmost_as_ghost=False):
211
        self.set_parent_ids([p for p, t in parents_list])
212
0.305.1 by Jelmer Vernooij
Reset RHS parents after commit.
213
    def _set_merges_from_parent_ids(self, rhs_parent_ids):
0.200.1779 by Jelmer Vernooij
Actually store git revision ids rather than bzr revision ids.
214
        try:
0.305.1 by Jelmer Vernooij
Reset RHS parents after commit.
215
            merges = [self.branch.lookup_bzr_revision_id(revid)[0] for revid in rhs_parent_ids]
0.200.1779 by Jelmer Vernooij
Actually store git revision ids rather than bzr revision ids.
216
        except errors.NoSuchRevision as e:
217
            raise errors.GhostRevisionUnusableHere(e.revision)
0.304.1 by Jelmer Vernooij
Delete MERGE_HEAD if there are no parent ids.
218
        if merges:
219
            self.control_transport.put_bytes('MERGE_HEAD', '\n'.join(merges),
220
                mode=self.controldir._get_file_mode())
221
        else:
222
            try:
223
                self.control_transport.delete('MERGE_HEAD')
224
            except errors.NoSuchFile:
225
                pass
0.200.1720 by Jelmer Vernooij
Fix handling of pending merges.
226
227
    def set_parent_ids(self, revision_ids, allow_leftmost_as_ghost=False):
228
        """Set the parent ids to revision_ids.
229
230
        See also set_parent_trees. This api will try to retrieve the tree data
231
        for each element of revision_ids from the trees repository. If you have
232
        tree data already available, it is more efficient to use
233
        set_parent_trees rather than set_parent_ids. set_parent_ids is however
234
        an easier API to use.
235
236
        :param revision_ids: The revision_ids to set as the parent ids of this
237
            working tree. Any of these may be ghosts.
238
        """
239
        with self.lock_tree_write():
240
            self._check_parents_for_ghosts(revision_ids,
241
                allow_leftmost_as_ghost=allow_leftmost_as_ghost)
242
            for revision_id in revision_ids:
243
                _mod_revision.check_not_reserved_id(revision_id)
244
245
            revision_ids = self._filter_parent_ids_by_ancestry(revision_ids)
246
247
            if len(revision_ids) > 0:
248
                self.set_last_revision(revision_ids[0])
249
            else:
250
                self.set_last_revision(_mod_revision.NULL_REVISION)
251
0.305.1 by Jelmer Vernooij
Reset RHS parents after commit.
252
            self._set_merges_from_parent_ids(revision_ids[1:])
0.200.1720 by Jelmer Vernooij
Fix handling of pending merges.
253
254
    def get_parent_ids(self):
255
        """See Tree.get_parent_ids.
256
257
        This implementation reads the pending merges list and last_revision
258
        value and uses that to decide what the parents list should be.
259
        """
260
        last_rev = _mod_revision.ensure_null(self._last_revision())
261
        if _mod_revision.NULL_REVISION == last_rev:
262
            parents = []
263
        else:
264
            parents = [last_rev]
265
        try:
266
            merges_bytes = self.control_transport.get_bytes('MERGE_HEAD')
267
        except errors.NoSuchFile:
268
            pass
269
        else:
270
            for l in osutils.split_lines(merges_bytes):
271
                revision_id = l.rstrip('\n')
0.200.1779 by Jelmer Vernooij
Actually store git revision ids rather than bzr revision ids.
272
                parents.append(self.branch.lookup_foreign_revision_id(revision_id))
0.200.1720 by Jelmer Vernooij
Fix handling of pending merges.
273
        return parents
274
0.200.1243 by Jelmer Vernooij
Implement WorkingTree.check_state.
275
    def check_state(self):
276
        """Check that the working state is/isn't valid."""
277
        pass
278
0.200.1215 by Jelmer Vernooij
Implement GitWorkingTree.remove.
279
    def remove(self, files, verbose=False, to_file=None, keep_files=True,
280
        force=False):
281
        """Remove nominated files from the working tree metadata.
282
283
        :param files: File paths relative to the basedir.
284
        :param keep_files: If true, the files will also be kept.
285
        :param force: Delete files and directories, even if they are changed
286
            and even if the directories are not empty.
287
        """
288
        if isinstance(files, basestring):
289
            files = [files]
290
291
        if to_file is None:
292
            to_file = sys.stdout
293
294
        def backup(file_to_backup):
295
            abs_path = self.abspath(file_to_backup)
0.200.1648 by Jelmer Vernooij
Fix compatibility with newer versions of breezy.
296
            backup_name = self.controldir._available_backup_name(file_to_backup)
0.200.1215 by Jelmer Vernooij
Implement GitWorkingTree.remove.
297
            osutils.rename(abs_path, self.abspath(backup_name))
298
            return "removed %s (but kept a copy: %s)" % (
299
                file_to_backup, backup_name)
300
0.357.1 by Jelmer Vernooij
Fix some remove tests.
301
        # Sort needed to first handle directory content before the directory
302
        files_to_backup = []
303
0.357.2 by Jelmer Vernooij
Fix more remove tests.
304
        all_files = set()
305
306
        def recurse_directory_to_add_files(directory):
307
            # Recurse directory and add all files
308
            # so we can check if they have changed.
309
            for parent_info, file_infos in self.walkdirs(directory):
310
                for relpath, basename, kind, lstat, fileid, kind in file_infos:
311
                    # Is it versioned or ignored?
312
                    if self.is_versioned(relpath):
313
                        # Add nested content for deletion.
314
                        all_files.add(relpath)
315
                    else:
316
                        # Files which are not versioned
317
                        # should be treated as unknown.
318
                        files_to_backup.append(relpath)
319
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
320
        with self.lock_tree_write():
0.357.2 by Jelmer Vernooij
Fix more remove tests.
321
            for filepath in files:
322
                # Get file name into canonical form.
323
                abspath = self.abspath(filepath)
324
                filepath = self.relpath(abspath)
325
326
                if filepath:
327
                    all_files.add(filepath)
328
                    recurse_directory_to_add_files(filepath)
329
330
            files = list(all_files)
331
332
            if len(files) == 0:
333
                return # nothing to do
334
335
            # Sort needed to first handle directory content before the directory
336
            files.sort(reverse=True)
337
0.357.1 by Jelmer Vernooij
Fix some remove tests.
338
            # Bail out if we are going to delete files we shouldn't
339
            if not keep_files and not force:
340
                for (file_id, path, content_change, versioned, parent_id, name,
341
                     kind, executable) in self.iter_changes(self.basis_tree(),
342
                         include_unchanged=True, require_versioned=False,
343
                         want_unversioned=True, specific_files=files):
344
                    if versioned[0] == False:
345
                        # The record is unknown or newly added
346
                        files_to_backup.append(path[1])
0.357.2 by Jelmer Vernooij
Fix more remove tests.
347
                        files_to_backup.extend(osutils.parent_directories(path[1]))
0.357.1 by Jelmer Vernooij
Fix some remove tests.
348
                    elif (content_change and (kind[1] is not None) and
349
                            osutils.is_inside_any(files, path[1])):
350
                        # Versioned and changed, but not deleted, and still
351
                        # in one of the dirs to be deleted.
352
                        files_to_backup.append(path[1])
0.357.2 by Jelmer Vernooij
Fix more remove tests.
353
                        files_to_backup.extend(osutils.parent_directories(path[1]))
354
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
355
            for f in files:
0.200.1735 by Jelmer Vernooij
Fix remove tests.
356
                if f == '':
357
                    continue
0.357.1 by Jelmer Vernooij
Fix some remove tests.
358
359
                try:
360
                    kind = self.kind(f)
361
                except errors.NoSuchFile:
362
                    kind = None
363
364
                abs_path = self.abspath(f)
365
                if verbose:
366
                    # having removed it, it must be either ignored or unknown
367
                    if self.is_ignored(f):
368
                        new_status = 'I'
369
                    else:
370
                        new_status = '?'
371
                    kind_ch = osutils.kind_marker(kind)
372
                    to_file.write(new_status + '       ' + f + kind_ch + '\n')
373
                if kind is None:
374
                    message = "%s does not exist" % (f, )
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
375
                else:
0.357.1 by Jelmer Vernooij
Fix some remove tests.
376
                    if not keep_files:
377
                        if f in files_to_backup and not force:
0.357.2 by Jelmer Vernooij
Fix more remove tests.
378
                            message = backup(f)
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
379
                        else:
0.357.1 by Jelmer Vernooij
Fix some remove tests.
380
                            if kind == 'directory':
381
                                osutils.rmtree(abs_path)
382
                            else:
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
383
                                osutils.delete_any(abs_path)
0.357.2 by Jelmer Vernooij
Fix more remove tests.
384
                            message = "deleted %s" % (f,)
385
                    else:
386
                        message = "removed %s" % (f,)
0.357.1 by Jelmer Vernooij
Fix some remove tests.
387
                self._unversion_path(f)
0.200.1215 by Jelmer Vernooij
Implement GitWorkingTree.remove.
388
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
389
                # print only one message (if any) per file.
390
                if message is not None:
391
                    trace.note(message)
0.357.1 by Jelmer Vernooij
Fix some remove tests.
392
            self._versioned_dirs = None
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
393
            self.flush()
0.200.1192 by Jelmer Vernooij
Implement path2id.
394
0.200.1240 by Jelmer Vernooij
Implement GitWorkingTree.smart_add.
395
    def smart_add(self, file_list, recurse=True, action=None, save=True):
0.200.1771 by Jelmer Vernooij
Fix implicit tree unicode add test.
396
        if not file_list:
397
            file_list = [u'.']
0.200.1781 by Jelmer Vernooij
Support expanding symlinks.
398
399
        # expand any symlinks in the directory part, while leaving the
400
        # filename alone
401
        # only expanding if symlinks are supported avoids windows path bugs
402
        if osutils.has_symlinks():
403
            file_list = list(map(osutils.normalizepath, file_list))
404
0.369.1 by Jelmer Vernooij
Implement conflict handling.
405
        conflicts_related = set()
406
        for c in self.conflicts():
407
            conflicts_related.update(c.associated_filenames())
408
0.200.1240 by Jelmer Vernooij
Implement GitWorkingTree.smart_add.
409
        added = []
410
        ignored = {}
411
        user_dirs = []
0.200.1733 by Jelmer Vernooij
Support handling of custom ids in smart_add.
412
        def call_action(filepath, kind):
413
            if action is not None:
414
                parent_path = posixpath.dirname(filepath)
415
                parent_id = self.path2id(parent_path)
416
                parent_ie = self._get_dir_ie(parent_path, parent_id)
417
                file_id = action(self, parent_ie, filepath, kind)
418
                if file_id is not None:
419
                    raise workingtree.SettingFileIdUnsupported()
420
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
421
        with self.lock_tree_write():
422
            for filepath in osutils.canonical_relpaths(self.basedir, file_list):
0.352.1 by Jelmer Vernooij
Check for normalization during add.
423
                filepath, can_access = osutils.normalized_filename(filepath)
424
                if not can_access:
425
                    raise errors.InvalidNormalization(filepath)
426
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
427
                abspath = self.abspath(filepath)
0.200.1240 by Jelmer Vernooij
Implement GitWorkingTree.smart_add.
428
                kind = osutils.file_kind(abspath)
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
429
                if kind in ("file", "symlink"):
0.369.2 by Jelmer Vernooij
Fix tests.
430
                    if filepath in self.index:
431
                        # Already present
432
                        continue
0.200.1733 by Jelmer Vernooij
Support handling of custom ids in smart_add.
433
                    call_action(filepath, kind)
0.200.1308 by Jelmer Vernooij
Write index to disk after adding files.
434
                    if save:
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
435
                        self._index_add_entry(filepath, kind)
436
                    added.append(filepath)
437
                elif kind == "directory":
0.369.2 by Jelmer Vernooij
Fix tests.
438
                    if filepath not in self.index:
439
                        call_action(filepath, kind)
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
440
                    if recurse:
441
                        user_dirs.append(filepath)
442
                else:
443
                    raise errors.BadFileKindError(filename=abspath, kind=kind)
444
            for user_dir in user_dirs:
445
                abs_user_dir = self.abspath(user_dir)
0.200.1752 by Jelmer Vernooij
Don't traverse nested trees in WorkingTree.smart_add.
446
                if user_dir != '':
447
                    try:
448
                        transport = _mod_transport.get_transport_from_path(abs_user_dir)
449
                        _mod_controldir.ControlDirFormat.find_format(transport)
450
                        subtree = True
451
                    except errors.NotBranchError:
452
                        subtree = False
453
                    except errors.UnsupportedFormatError:
454
                        subtree = False
455
                else:
456
                    subtree = False
457
                if subtree:
0.200.1769 by Jelmer Vernooij
Raise proper error when encountering nested trees.
458
                    trace.warning('skipping nested tree %r', abs_user_dir)
0.200.1752 by Jelmer Vernooij
Don't traverse nested trees in WorkingTree.smart_add.
459
                    continue
460
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
461
                for name in os.listdir(abs_user_dir):
462
                    subp = os.path.join(user_dir, name)
463
                    if self.is_control_filename(subp) or self.mapping.is_special_file(subp):
464
                        continue
465
                    ignore_glob = self.is_ignored(subp)
466
                    if ignore_glob is not None:
467
                        ignored.setdefault(ignore_glob, []).append(subp)
468
                        continue
469
                    abspath = self.abspath(subp)
470
                    kind = osutils.file_kind(abspath)
471
                    if kind == "directory":
472
                        user_dirs.append(subp)
473
                    else:
0.369.2 by Jelmer Vernooij
Fix tests.
474
                        if subp in self.index:
475
                            # Already present
476
                            continue
0.369.3 by Jelmer Vernooij
Fix another conflict test.
477
                        if subp in conflicts_related:
478
                            continue
0.200.1733 by Jelmer Vernooij
Support handling of custom ids in smart_add.
479
                        call_action(filepath, kind)
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
480
                        if save:
481
                            self._index_add_entry(subp, kind)
0.200.1751 by Jelmer Vernooij
Fix smart_add return value.
482
                        added.append(subp)
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
483
            if added and save:
484
                self.flush()
485
            return added, ignored
0.200.1240 by Jelmer Vernooij
Implement GitWorkingTree.smart_add.
486
0.200.1712 by Jelmer Vernooij
Add file_id prefix.
487
    def has_filename(self, filename):
488
        return osutils.lexists(self.abspath(filename))
489
0.200.1766 by Jelmer Vernooij
Fix another list_files test, integrate is_versioned.
490
    def _iter_files_recursive(self, from_dir=None, include_dirs=False):
0.200.1328 by Jelmer Vernooij
More test fixes.
491
        if from_dir is None:
0.379.1 by Jelmer Vernooij
Improve unicode handling in GitWorkingTree.
492
            from_dir = u""
0.341.1 by Jelmer Vernooij
Fix BadFilenameEncoding raising during WorkingTree.extras.
493
        for (dirpath, dirnames, filenames) in os.walk(self.abspath(from_dir).encode(osutils._fs_enc)):
0.200.1302 by Jelmer Vernooij
Significantly improve performance of WorkingTree.extras().
494
            dir_relpath = dirpath[len(self.basedir):].strip("/")
0.200.1648 by Jelmer Vernooij
Fix compatibility with newer versions of breezy.
495
            if self.controldir.is_control_filename(dir_relpath):
0.200.605 by Jelmer Vernooij
Ignore directories in WorkingTree.extras().
496
                continue
0.200.1766 by Jelmer Vernooij
Fix another list_files test, integrate is_versioned.
497
            for name in list(dirnames):
498
                if self.controldir.is_control_filename(name):
499
                    dirnames.remove(name)
500
                    continue
501
                relpath = os.path.join(dir_relpath, name)
502
                if include_dirs:
0.341.1 by Jelmer Vernooij
Fix BadFilenameEncoding raising during WorkingTree.extras.
503
                    try:
504
                        yield relpath.decode(osutils._fs_enc)
0.379.1 by Jelmer Vernooij
Improve unicode handling in GitWorkingTree.
505
                    except UnicodeDecodeError:
0.341.1 by Jelmer Vernooij
Fix BadFilenameEncoding raising during WorkingTree.extras.
506
                        raise errors.BadFilenameEncoding(
507
                            relpath, osutils._fs_enc)
0.200.1766 by Jelmer Vernooij
Fix another list_files test, integrate is_versioned.
508
                if not self._has_dir(relpath):
509
                    dirnames.remove(name)
510
            for name in filenames:
511
                if not self.mapping.is_special_file(name):
0.341.1 by Jelmer Vernooij
Fix BadFilenameEncoding raising during WorkingTree.extras.
512
                    yp = os.path.join(dir_relpath, name)
513
                    try:
514
                        yield yp.decode(osutils._fs_enc)
515
                    except UnicodeDecodeError:
516
                        raise errors.BadFilenameEncoding(
517
                            yp, osutils._fs_enc)
0.200.1327 by Jelmer Vernooij
Factor out all file browsing in extras.
518
519
    def extras(self):
520
        """Yield all unversioned files in this WorkingTree.
521
        """
0.200.1676 by Jelmer Vernooij
Fix typo.
522
        with self.lock_read():
0.379.1 by Jelmer Vernooij
Improve unicode handling in GitWorkingTree.
523
            for p in (set(self._iter_files_recursive(include_dirs=True)) - set([p.decode('utf-8') for p in self.index])):
524
                if not self._has_dir(p):
525
                    yield p
0.200.605 by Jelmer Vernooij
Ignore directories in WorkingTree.extras().
526
0.200.382 by Jelmer Vernooij
Support flushing index.
527
    def flush(self):
0.287.6 by Jelmer Vernooij
Fix some more tests.
528
        # TODO: Maybe this should only write on dirty ?
529
        if self._lock_mode != 'w':
530
            raise errors.NotWriteLocked(self)
531
        self.index.write()
0.200.382 by Jelmer Vernooij
Support flushing index.
532
0.200.1202 by Jelmer Vernooij
Implement has_or_had_id.
533
    def has_or_had_id(self, file_id):
534
        if self.has_id(file_id):
535
            return True
536
        if self.had_id(file_id):
537
            return True
538
        return False
539
540
    def had_id(self, file_id):
541
        path = self._basis_fileid_map.lookup_file_id(file_id)
542
        try:
543
            head = self.repository._git.head()
544
        except KeyError:
545
            # Assume no if basis is not accessible
546
            return False
0.336.2 by Jelmer Vernooij
Don't use ZERO_SHA outside of remote communication to indicate empty history.
547
        try:
548
            root_tree = self.store[head].tree
549
        except KeyError:
0.200.1205 by Jelmer Vernooij
Implement GitWorkingTree.stored_kind.
550
            return False
0.200.1202 by Jelmer Vernooij
Implement has_or_had_id.
551
        try:
552
            tree_lookup_path(self.store.__getitem__, root_tree, path)
553
        except KeyError:
554
            return False
555
        else:
556
            return True
557
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
558
    def get_file_mtime(self, path, file_id=None):
0.200.1200 by Jelmer Vernooij
Support GitWorkingTree.get_file_mtime.
559
        """See Tree.get_file_mtime."""
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
560
        try:
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
561
            return self._lstat(path).st_mtime
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
562
        except OSError, (num, msg):
563
            if num == errno.ENOENT:
564
                raise errors.NoSuchFile(path)
565
            raise
0.200.1200 by Jelmer Vernooij
Support GitWorkingTree.get_file_mtime.
566
0.200.1655 by Jelmer Vernooij
Basic support for git ignores.
567
    def is_ignored(self, filename):
568
        r"""Check whether the filename matches an ignore pattern.
569
570
        If the file is ignored, returns the pattern which caused it to
571
        be ignored, otherwise None.  So this can simply be used as a
572
        boolean if desired."""
573
        if getattr(self, '_global_ignoreglobster', None) is None:
574
            ignore_globs = set()
575
            ignore_globs.update(ignores.get_runtime_ignores())
576
            ignore_globs.update(ignores.get_user_ignores())
577
            self._global_ignoreglobster = globbing.ExceptionGlobster(ignore_globs)
0.200.1656 by Jelmer Vernooij
Report proper patterns, ignore files.
578
        match = self._global_ignoreglobster.match(filename)
579
        if match is not None:
580
            return match
0.200.1716 by Jelmer Vernooij
Fix some more tests.
581
        try:
582
            if self.kind(filename) == 'directory':
583
                filename += b'/'
584
        except errors.NoSuchFile:
585
            pass
586
        filename = filename.lstrip(b'/')
0.200.1658 by Jelmer Vernooij
Fix handling of ignores - return patterns that matched.
587
        ignore_manager = self._get_ignore_manager()
588
        ps = list(ignore_manager.find_matching(filename))
589
        if not ps:
590
            return None
591
        if not ps[-1].is_exclude:
592
            return None
593
        return bytes(ps[-1])
594
595
    def _get_ignore_manager(self):
596
        ignoremanager = getattr(self, '_ignoremanager', None)
597
        if ignoremanager is not None:
598
            return ignoremanager
599
600
        ignore_manager = IgnoreFilterManager.from_repo(self.repository._git)
601
        self._ignoremanager = ignore_manager
602
        return ignore_manager
0.200.409 by Jelmer Vernooij
Support parsing .gitignore.
603
0.200.1716 by Jelmer Vernooij
Fix some more tests.
604
    def _flush_ignore_list_cache(self):
605
        self._ignoremanager = None
606
0.200.508 by Jelmer Vernooij
Skip inventory caching bits.
607
    def set_last_revision(self, revid):
0.200.1720 by Jelmer Vernooij
Fix handling of pending merges.
608
        if _mod_revision.is_null(revid):
609
            self.branch.set_last_revision_info(0, revid)
610
            return False
611
        _mod_revision.check_not_reserved_id(revid)
612
        try:
613
            self.branch.generate_revision_history(revid)
614
        except errors.NoSuchRevision:
615
            raise errors.GhostRevisionUnusableHere(revid)
0.200.508 by Jelmer Vernooij
Skip inventory caching bits.
616
0.200.379 by Jelmer Vernooij
Re-enable working tree support.
617
    def _reset_data(self):
0.248.3 by Jelmer Vernooij
Handle working trees without valid HEAD branch.
618
        try:
619
            head = self.repository._git.head()
0.336.2 by Jelmer Vernooij
Don't use ZERO_SHA outside of remote communication to indicate empty history.
620
        except KeyError:
0.200.1202 by Jelmer Vernooij
Implement has_or_had_id.
621
            self._basis_fileid_map = GitFileIdMap({}, self.mapping)
0.200.948 by Jelmer Vernooij
Cope with empty inventories.
622
        else:
0.336.2 by Jelmer Vernooij
Don't use ZERO_SHA outside of remote communication to indicate empty history.
623
            self._basis_fileid_map = self.mapping.get_fileid_map(
624
                self.store.__getitem__, self.store[head].tree)
0.371.1 by Jelmer Vernooij
Implement WorkingTree.reset_state().
625
        self._fileid_map = self._basis_fileid_map.copy()
0.200.379 by Jelmer Vernooij
Re-enable working tree support.
626
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
627
    def get_file_verifier(self, path, file_id=None, stat_value=None):
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
628
        with self.lock_read():
629
            try:
0.379.1 by Jelmer Vernooij
Improve unicode handling in GitWorkingTree.
630
                return ("GIT", self.index[path.encode('utf-8')].sha)
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
631
            except KeyError:
632
                if self._has_dir(path):
633
                    return ("GIT", None)
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
634
                raise errors.NoSuchFile(path)
0.200.1302 by Jelmer Vernooij
Significantly improve performance of WorkingTree.extras().
635
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
636
    def get_file_sha1(self, path, file_id=None, stat_value=None):
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
637
        with self.lock_read():
0.200.1772 by Jelmer Vernooij
Fix handling of unversioned file in get_file_sha1.
638
            if not self.is_versioned(path):
639
                raise errors.NoSuchFile(path)
640
            abspath = self.abspath(path)
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
641
            try:
642
                return osutils.sha_file_by_name(abspath)
643
            except OSError, (num, msg):
644
                if num in (errno.EISDIR, errno.ENOENT):
0.200.1772 by Jelmer Vernooij
Fix handling of unversioned file in get_file_sha1.
645
                    return None
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
646
                raise
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
647
0.200.610 by Jelmer Vernooij
Support retrieving basis tree properly.
648
    def revision_tree(self, revid):
649
        return self.repository.revision_tree(revid)
650
0.200.1239 by Jelmer Vernooij
Implement GitWorkingTree.filter_unversioned_files.
651
    def filter_unversioned_files(self, files):
0.200.1766 by Jelmer Vernooij
Fix another list_files test, integrate is_versioned.
652
        return set([p for p in files if not self.is_versioned(p)])
0.200.1239 by Jelmer Vernooij
Implement GitWorkingTree.filter_unversioned_files.
653
0.264.11 by Jelmer Vernooij
Completer implementation of iter_entries_by_dir and list_files.
654
    def _is_executable_from_path_and_stat_from_stat(self, path, stat_result):
655
        mode = stat_result.st_mode
656
        return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
657
0.318.1 by Jelmer Vernooij
Fix executable test.
658
    def _is_executable_from_path_and_stat_from_basis(self, path, stat_result):
659
        return self.basis_tree().is_executable(path)
660
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
661
    def stored_kind(self, path, file_id=None):
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
662
        with self.lock_read():
663
            try:
0.379.1 by Jelmer Vernooij
Improve unicode handling in GitWorkingTree.
664
                return mode_kind(self.index[path.encode("utf-8")].mode)
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
665
            except KeyError:
666
                # Maybe it's a directory?
667
                if self._has_dir(path):
668
                    return "directory"
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
669
                raise errors.NoSuchFile(path)
0.200.1205 by Jelmer Vernooij
Implement GitWorkingTree.stored_kind.
670
0.360.1 by Jelmer Vernooij
Implement GitMemoryTree.
671
    def _lstat(self, path):
672
        return os.lstat(self.abspath(path))
673
0.285.1 by Jelmer Vernooij
Swap arguments for tree methods.
674
    def is_executable(self, path, file_id=None):
0.379.1 by Jelmer Vernooij
Improve unicode handling in GitWorkingTree.
675
        with self.lock_read():
676
            if getattr(self, "_supports_executable", osutils.supports_executable)():
677
                mode = self._lstat(path).st_mode
678
            else:
679
                try:
680
                    mode = self.index[path.encode('utf-8')].mode
681
                except KeyError:
682
                    mode = 0
683
            return bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
0.264.11 by Jelmer Vernooij
Completer implementation of iter_entries_by_dir and list_files.
684
0.200.1539 by Jelmer Vernooij
Cope with new is_executable.
685
    def _is_executable_from_path_and_stat(self, path, stat_result):
686
        if getattr(self, "_supports_executable", osutils.supports_executable)():
687
            return self._is_executable_from_path_and_stat_from_stat(path, stat_result)
688
        else:
689
            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.
690
0.264.10 by Jelmer Vernooij
Yield inventory entries.
691
    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.
692
        if from_dir is None:
0.379.1 by Jelmer Vernooij
Improve unicode handling in GitWorkingTree.
693
            from_dir = u""
0.264.11 by Jelmer Vernooij
Completer implementation of iter_entries_by_dir and list_files.
694
        dir_ids = {}
0.200.1767 by Jelmer Vernooij
Fix references to Tree*.
695
        fk_entries = {'directory': tree.TreeDirectory,
696
                      'file': tree.TreeFile,
697
                      'symlink': tree.TreeLink}
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
698
        with self.lock_read():
699
            root_ie = self._get_dir_ie(u"", None)
700
            if include_root and not from_dir:
701
                yield "", "V", root_ie.kind, root_ie.file_id, root_ie
702
            dir_ids[u""] = root_ie.file_id
703
            if recursive:
0.200.1766 by Jelmer Vernooij
Fix another list_files test, integrate is_versioned.
704
                path_iterator = sorted(self._iter_files_recursive(from_dir, include_dirs=True))
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
705
            else:
0.379.1 by Jelmer Vernooij
Improve unicode handling in GitWorkingTree.
706
                path_iterator = sorted([os.path.join(from_dir, name.decode(osutils._fs_enc)) for name in
707
                    os.listdir(self.abspath(from_dir).encode(osutils._fs_enc)) if not self.controldir.is_control_filename(name)
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
708
                    and not self.mapping.is_special_file(name)])
709
            for path in path_iterator:
710
                try:
0.379.1 by Jelmer Vernooij
Improve unicode handling in GitWorkingTree.
711
                    index_path = path.encode("utf-8")
712
                except UnicodeEncodeError:
713
                    raise errors.BadFilenameEncoding(
714
                        path, osutils._fs_enc)
715
                try:
716
                    value = self.index[index_path]
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
717
                except KeyError:
718
                    value = None
0.200.1766 by Jelmer Vernooij
Fix another list_files test, integrate is_versioned.
719
                kind = osutils.file_kind(self.abspath(path))
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
720
                parent, name = posixpath.split(path)
721
                for dir_path, dir_ie in self._add_missing_parent_ids(parent, dir_ids):
0.200.1766 by Jelmer Vernooij
Fix another list_files test, integrate is_versioned.
722
                    pass
723
                if kind == 'directory':
724
                    if path != from_dir:
725
                        if self._has_dir(path):
726
                            ie = self._get_dir_ie(path, self.path2id(path))
727
                            status = "V"
728
                            file_id = ie.file_id
729
                        elif self.is_ignored(path):
730
                            status = "I"
731
                            ie = fk_entries[kind]()
732
                            file_id = None
733
                        else:
734
                            status = "?"
735
                            ie = fk_entries[kind]()
736
                            file_id = None
737
                        yield posixpath.relpath(path, from_dir), status, kind, file_id, ie
738
                    continue
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
739
                if value is not None:
740
                    ie = self._get_file_ie(name, path, value, dir_ids[parent])
0.200.1766 by Jelmer Vernooij
Fix another list_files test, integrate is_versioned.
741
                    yield posixpath.relpath(path, from_dir), "V", ie.kind, ie.file_id, ie
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
742
                else:
743
                    ie = fk_entries[kind]()
0.200.1766 by Jelmer Vernooij
Fix another list_files test, integrate is_versioned.
744
                    yield posixpath.relpath(path, from_dir), ("I" if self.is_ignored(path) else "?"), kind, None, ie
0.264.10 by Jelmer Vernooij
Yield inventory entries.
745
0.200.1206 by Jelmer Vernooij
Implement GitWorkingTree.all_file_ids.
746
    def all_file_ids(self):
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
747
        with self.lock_read():
748
            ids = {u"": self.path2id("")}
749
            for path in self.index:
750
                if self.mapping.is_special_file(path):
751
                    continue
752
                path = path.decode("utf-8")
753
                parent = posixpath.dirname(path).strip("/")
754
                for e in self._add_missing_parent_ids(parent, ids):
755
                    pass
756
                ids[path] = self.path2id(path)
757
            return set(ids.values())
0.200.1206 by Jelmer Vernooij
Implement GitWorkingTree.all_file_ids.
758
0.200.1710 by Jelmer Vernooij
Regenerate xfail.
759
    def all_versioned_paths(self):
760
        with self.lock_read():
761
            paths = {u""}
762
            for path in self.index:
763
                if self.mapping.is_special_file(path):
764
                    continue
765
                path = path.decode("utf-8")
766
                paths.add(path)
767
                while path != "":
768
                    path = posixpath.dirname(path).strip("/")
769
                    if path in paths:
770
                        break
771
                    paths.add(path)
772
            return paths
773
0.200.1374 by Jelmer Vernooij
Implement GitWorkingTree._directory_is_tree_reference.
774
    def _directory_is_tree_reference(self, path):
775
        # FIXME: Check .gitsubmodules for path
776
        return False
777
0.200.1716 by Jelmer Vernooij
Fix some more tests.
778
    def iter_child_entries(self, path, file_id=None):
779
        encoded_path = path.encode('utf-8')
0.379.1 by Jelmer Vernooij
Improve unicode handling in GitWorkingTree.
780
        with self.lock_read():
781
            parent_id = self.path2id(path)
782
            found_any = False
783
            seen_children = set()
784
            for item_path, value in self.index.iteritems():
785
                if self.mapping.is_special_file(item_path):
786
                    continue
787
                if not osutils.is_inside(encoded_path, item_path):
788
                    continue
789
                found_any = True
790
                subpath = posixpath.relpath(item_path, encoded_path)
791
                if b'/' in subpath:
792
                    dirname = subpath.split(b'/', 1)[0]
793
                    file_ie = self._get_dir_ie(posixpath.join(path, dirname), parent_id)
794
                else:
795
                    (parent, name) = posixpath.split(item_path)
796
                    file_ie = self._get_file_ie(
797
                            name.decode('utf-8'),
798
                            item_path.decode('utf-8'), value, parent_id)
799
                yield file_ie
800
            if not found_any and path != u'':
801
                raise errors.NoSuchFile(path)
0.200.1716 by Jelmer Vernooij
Fix some more tests.
802
0.200.619 by Jelmer Vernooij
Provide dummy WorkingTree.conflicts() implementation rather than spending a lot of time not finding any conflicts.
803
    def conflicts(self):
0.200.1675 by Jelmer Vernooij
Remove uses of decorators.
804
        with self.lock_read():
0.369.2 by Jelmer Vernooij
Fix tests.
805
            conflicts = _mod_conflicts.ConflictList()
0.369.1 by Jelmer Vernooij
Implement conflict handling.
806
            for item_path, value in self.index.iteritems():
807
                if value.flags & FLAG_STAGEMASK:
808
                    conflicts.append(_mod_conflicts.TextConflict(item_path.decode('utf-8')))
809
            return conflicts
810
811
    def set_conflicts(self, conflicts):
812
        by_path = set()
813
        for conflict in conflicts:
814
            if conflict.typestring in ('text conflict', 'contents conflict'):
815
                by_path.add(conflict.path.encode('utf-8'))
816
            else:
817
                raise errors.UnsupportedOperation(self.set_conflicts, self)
818
        with self.lock_tree_write():
819
            for path in self.index:
820
                self._set_conflicted(path, path in by_path)
821
            self.flush()
822
823
    def _set_conflicted(self, path, conflicted):
824
        trace.mutter('change conflict: %r -> %r', path, conflicted)
825
        value = self.index[path]
826
        if conflicted:
827
            self.index[path] = (value[:9] + (value[9] | FLAG_STAGEMASK, ))
828
        else:
829
            self.index[path] = (value[:9] + (value[9] &~ FLAG_STAGEMASK, ))
830
831
    def add_conflicts(self, new_conflicts):
832
        with self.lock_tree_write():
833
            for conflict in new_conflicts:
834
                if conflict.typestring in ('text conflict', 'contents conflict'):
835
                    try:
836
                        self._set_conflicted(conflict.path.encode('utf-8'), True)
837
                    except KeyError:
838
                        raise errors.UnsupportedOperation(self.add_conflicts, self)
839
                else:
840
                    raise errors.UnsupportedOperation(self.add_conflicts, self)
841
            self.flush()
0.200.619 by Jelmer Vernooij
Provide dummy WorkingTree.conflicts() implementation rather than spending a lot of time not finding any conflicts.
842
0.200.1705 by Jelmer Vernooij
Fix walkdirs.
843
    def walkdirs(self, prefix=""):
0.324.1 by Jelmer Vernooij
initial work fixing walkdirs.
844
        """Walk the directories of this tree.
845
846
        returns a generator which yields items in the form:
847
                ((curren_directory_path, fileid),
848
                 [(file1_path, file1_name, file1_kind, (lstat), file1_id,
849
                   file1_kind), ... ])
850
851
        This API returns a generator, which is only valid during the current
852
        tree transaction - within a single lock_read or lock_write duration.
853
854
        If the tree is not locked, it may cause an error to be raised,
855
        depending on the tree implementation.
856
        """
857
        from bisect import bisect_left
858
        import operator
859
        disk_top = self.abspath(prefix)
860
        if disk_top.endswith('/'):
861
            disk_top = disk_top[:-1]
862
        top_strip_len = len(disk_top) + 1
863
        inventory_iterator = self._walkdirs(prefix)
864
        disk_iterator = osutils.walkdirs(disk_top, prefix)
865
        try:
866
            current_disk = next(disk_iterator)
867
            disk_finished = False
868
        except OSError as e:
869
            if not (e.errno == errno.ENOENT or
870
                (sys.platform == 'win32' and e.errno == ERROR_PATH_NOT_FOUND)):
871
                raise
872
            current_disk = None
873
            disk_finished = True
874
        try:
875
            current_inv = next(inventory_iterator)
876
            inv_finished = False
877
        except StopIteration:
878
            current_inv = None
879
            inv_finished = True
880
        while not inv_finished or not disk_finished:
881
            if current_disk:
882
                ((cur_disk_dir_relpath, cur_disk_dir_path_from_top),
883
                    cur_disk_dir_content) = current_disk
884
            else:
885
                ((cur_disk_dir_relpath, cur_disk_dir_path_from_top),
886
                    cur_disk_dir_content) = ((None, None), None)
887
            if not disk_finished:
888
                # strip out .bzr dirs
889
                if (cur_disk_dir_path_from_top[top_strip_len:] == '' and
890
                    len(cur_disk_dir_content) > 0):
891
                    # osutils.walkdirs can be made nicer -
892
                    # yield the path-from-prefix rather than the pathjoined
893
                    # value.
894
                    bzrdir_loc = bisect_left(cur_disk_dir_content,
895
                        ('.git', '.git'))
896
                    if (bzrdir_loc < len(cur_disk_dir_content)
897
                        and self.controldir.is_control_filename(
898
                            cur_disk_dir_content[bzrdir_loc][0])):
899
                        # we dont yield the contents of, or, .bzr itself.
900
                        del cur_disk_dir_content[bzrdir_loc]
901
            if inv_finished:
902
                # everything is unknown
903
                direction = 1
904
            elif disk_finished:
905
                # everything is missing
906
                direction = -1
907
            else:
908
                direction = cmp(current_inv[0][0], cur_disk_dir_relpath)
909
            if direction > 0:
910
                # disk is before inventory - unknown
911
                dirblock = [(relpath, basename, kind, stat, None, None) for
912
                    relpath, basename, kind, stat, top_path in
913
                    cur_disk_dir_content]
914
                yield (cur_disk_dir_relpath, None), dirblock
915
                try:
916
                    current_disk = next(disk_iterator)
917
                except StopIteration:
918
                    disk_finished = True
919
            elif direction < 0:
920
                # inventory is before disk - missing.
921
                dirblock = [(relpath, basename, 'unknown', None, fileid, kind)
922
                    for relpath, basename, dkind, stat, fileid, kind in
923
                    current_inv[1]]
924
                yield (current_inv[0][0], current_inv[0][1]), dirblock
925
                try:
926
                    current_inv = next(inventory_iterator)
927
                except StopIteration:
928
                    inv_finished = True
929
            else:
930
                # versioned present directory
931
                # merge the inventory and disk data together
932
                dirblock = []
933
                for relpath, subiterator in itertools.groupby(sorted(
934
                    current_inv[1] + cur_disk_dir_content,
935
                    key=operator.itemgetter(0)), operator.itemgetter(1)):
936
                    path_elements = list(subiterator)
937
                    if len(path_elements) == 2:
938
                        inv_row, disk_row = path_elements
939
                        # versioned, present file
940
                        dirblock.append((inv_row[0],
941
                            inv_row[1], disk_row[2],
942
                            disk_row[3], inv_row[4],
943
                            inv_row[5]))
944
                    elif len(path_elements[0]) == 5:
945
                        # unknown disk file
946
                        dirblock.append((path_elements[0][0],
947
                            path_elements[0][1], path_elements[0][2],
948
                            path_elements[0][3], None, None))
949
                    elif len(path_elements[0]) == 6:
950
                        # versioned, absent file.
951
                        dirblock.append((path_elements[0][0],
952
                            path_elements[0][1], 'unknown', None,
953
                            path_elements[0][4], path_elements[0][5]))
954
                    else:
955
                        raise NotImplementedError('unreachable code')
956
                yield current_inv[0], dirblock
957
                try:
958
                    current_inv = next(inventory_iterator)
959
                except StopIteration:
960
                    inv_finished = True
961
                try:
962
                    current_disk = next(disk_iterator)
963
                except StopIteration:
964
                    disk_finished = True
965
966
    def _walkdirs(self, prefix=""):
0.200.1210 by Jelmer Vernooij
Implement GitWorkingTree._walkdirs.
967
        if prefix != "":
968
            prefix += "/"
0.379.1 by Jelmer Vernooij
Improve unicode handling in GitWorkingTree.
969
        prefix = prefix.encode('utf-8')
0.353.1 by Jelmer Vernooij
Iterate over dirs in _walkdirs.
970
        per_dir = defaultdict(set)
0.325.2 by Jelmer Vernooij
Handle root in walkdirs.
971
        if prefix == "":
0.353.1 by Jelmer Vernooij
Iterate over dirs in _walkdirs.
972
            per_dir[('', self.get_root_id())] = set()
973
        def add_entry(path, kind):
974
            if path == '' or not path.startswith(prefix):
975
                return
0.200.1210 by Jelmer Vernooij
Implement GitWorkingTree._walkdirs.
976
            (dirname, child_name) = posixpath.split(path)
0.353.1 by Jelmer Vernooij
Iterate over dirs in _walkdirs.
977
            add_entry(dirname, 'directory')
0.200.1210 by Jelmer Vernooij
Implement GitWorkingTree._walkdirs.
978
            dirname = dirname.decode("utf-8")
979
            dir_file_id = self.path2id(dirname)
0.361.2 by Jelmer Vernooij
Fix up assert.
980
            if not isinstance(value, tuple) or len(value) != 10:
0.361.1 by Jelmer Vernooij
Don't use assert.
981
                raise ValueError(value)
0.353.1 by Jelmer Vernooij
Iterate over dirs in _walkdirs.
982
            per_dir[(dirname, dir_file_id)].add(
0.200.1210 by Jelmer Vernooij
Implement GitWorkingTree._walkdirs.
983
                (path.decode("utf-8"), child_name.decode("utf-8"),
0.353.1 by Jelmer Vernooij
Iterate over dirs in _walkdirs.
984
                kind, None,
0.200.1210 by Jelmer Vernooij
Implement GitWorkingTree._walkdirs.
985
                self.path2id(path.decode("utf-8")),
0.353.1 by Jelmer Vernooij
Iterate over dirs in _walkdirs.
986
                kind))
0.379.1 by Jelmer Vernooij
Improve unicode handling in GitWorkingTree.
987
        with self.lock_read():
988
            for path, value in self.index.iteritems():
989
                if self.mapping.is_special_file(path):
990
                    continue
991
                if not path.startswith(prefix):
992
                    continue
993
                add_entry(path, mode_kind(value.mode))
0.353.1 by Jelmer Vernooij
Iterate over dirs in _walkdirs.
994
        return ((k, sorted(v)) for (k, v) in sorted(per_dir.iteritems()))
0.200.1542 by Jelmer Vernooij
Refactor iter_changes.
995
0.200.1677 by Jelmer Vernooij
Mark shelving as unsupported.
996
    def get_shelf_manager(self):
0.200.1729 by Jelmer Vernooij
ShelvingUnsupported doesn't take an argument.
997
        raise workingtree.ShelvingUnsupported()
0.200.1677 by Jelmer Vernooij
Mark shelving as unsupported.
998
0.200.1678 by Jelmer Vernooij
Fix tests.
999
    def store_uncommitted(self):
1000
        raise errors.StoringUncommittedNotSupported(self)
1001
0.200.1703 by Jelmer Vernooij
Implement apply_inventory_delta.
1002
    def apply_inventory_delta(self, changes):
1003
        for (old_path, new_path, file_id, ie) in changes:
1004
            if old_path is not None:
0.385.1 by Jelmer Vernooij
Use specific_files argument to Tree.iter_entries_by_dir.
1005
                try:
1006
                    del self.index[old_path.encode('utf-8')]
1007
                except KeyError:
1008
                    pass
1009
                else:
1010
                    self._versioned_dirs = None
0.200.1703 by Jelmer Vernooij
Implement apply_inventory_delta.
1011
            if new_path is not None and ie.kind != 'directory':
1012
                self._index_add_entry(new_path, ie.kind)
0.316.2 by Jelmer Vernooij
Flush after modifying index.
1013
        self.flush()
0.200.1703 by Jelmer Vernooij
Implement apply_inventory_delta.
1014
0.200.1756 by Jelmer Vernooij
Initial work on annotate support.
1015
    def annotate_iter(self, path, file_id=None,
1016
                      default_revision=_mod_revision.CURRENT_REVISION):
1017
        """See Tree.annotate_iter
1018
1019
        This implementation will use the basis tree implementation if possible.
1020
        Lines not in the basis are attributed to CURRENT_REVISION
1021
1022
        If there are pending merges, lines added by those merges will be
1023
        incorrectly attributed to CURRENT_REVISION (but after committing, the
1024
        attribution will be correct).
1025
        """
1026
        with self.lock_read():
1027
            maybe_file_parent_keys = []
1028
            for parent_id in self.get_parent_ids():
1029
                try:
1030
                    parent_tree = self.revision_tree(parent_id)
1031
                except errors.NoSuchRevisionInTree:
1032
                    parent_tree = self.branch.repository.revision_tree(
1033
                            parent_id)
1034
                with parent_tree.lock_read():
1035
                    # TODO(jelmer): Use rename/copy tracker to find path name in parent
1036
                    parent_path = path
1037
                    try:
1038
                        kind = parent_tree.kind(parent_path)
1039
                    except errors.NoSuchFile:
1040
                        continue
1041
                    if kind != 'file':
1042
                        # Note: this is slightly unnecessary, because symlinks and
1043
                        # directories have a "text" which is the empty text, and we
1044
                        # know that won't mess up annotations. But it seems cleaner
1045
                        continue
1046
                    parent_text_key = (
1047
                        parent_path,
1048
                        parent_tree.get_file_revision(parent_path))
1049
                    if parent_text_key not in maybe_file_parent_keys:
1050
                        maybe_file_parent_keys.append(parent_text_key)
1051
            graph = self.branch.repository.get_file_graph()
1052
            heads = graph.heads(maybe_file_parent_keys)
1053
            file_parent_keys = []
1054
            for key in maybe_file_parent_keys:
1055
                if key in heads:
1056
                    file_parent_keys.append(key)
1057
1058
            # Now we have the parents of this content
1059
            from breezy.annotate import Annotator
1060
            from .annotate import AnnotateProvider
1061
            annotator = Annotator(AnnotateProvider(
1062
                self.branch.repository._file_change_scanner))
1063
            text = self.get_file_text(path)
1064
            this_key = (path, default_revision)
1065
            annotator.add_special_text(this_key, file_parent_keys, text)
1066
            annotations = [(key[-1], line)
1067
                           for key, line in annotator.annotate_flat(this_key)]
1068
            return annotations
1069
0.360.4 by Jelmer Vernooij
Implement MemoryTree.rename_one, MemoryTree.mkdir.
1070
    def _rename_one(self, from_rel, to_rel):
1071
        os.rename(self.abspath(from_rel), self.abspath(to_rel))
1072
0.373.1 by Jelmer Vernooij
Fix WorkingTree.reset_state().
1073
    def _build_checkout_with_index(self):
1074
        build_index_from_tree(
1075
            self.user_transport.local_abspath('.'),
1076
            self.control_transport.local_abspath("index"),
1077
            self.store,
1078
            None if self.branch.head is None else self.store[self.branch.head].tree)
1079
0.371.1 by Jelmer Vernooij
Implement WorkingTree.reset_state().
1080
    def reset_state(self, revision_ids=None):
1081
        """Reset the state of the working tree.
1082
1083
        This does a hard-reset to a last-known-good state. This is a way to
1084
        fix if something got corrupted (like the .git/index file)
1085
        """
1086
        with self.lock_tree_write():
1087
            if revision_ids is not None:
1088
                self.set_parent_ids(revision_ids)
0.373.1 by Jelmer Vernooij
Fix WorkingTree.reset_state().
1089
            self.index.clear()
1090
            if self.branch.head is not None:
1091
                for entry in self.store.iter_tree_contents(self.store[self.branch.head].tree):
1092
                    if not validate_path(entry.path):
1093
                        continue
1094
1095
                    if S_ISGITLINK(entry.mode):
1096
                        pass # TODO(jelmer): record and return submodule paths
1097
                    else:
1098
                        # Let's at least try to use the working tree file:
1099
                        try:
1100
                            st = self._lstat(self.abspath(entry.path))
1101
                        except OSError, (num, msg):
1102
                            # But if it doesn't exist, we'll make something up.
1103
                            obj = self.store[entry.sha]
1104
                            st = os.stat_result((entry.mode, 0, 0, 0,
1105
                                  0, 0, len(obj.as_raw_string()), 0,
1106
                                  0, 0))
1107
                    self.index[entry.path] = index_entry_from_stat(st, entry.sha, 0)
1108
            self.flush()
0.371.1 by Jelmer Vernooij
Implement WorkingTree.reset_state().
1109
0.380.1 by Jelmer Vernooij
Implement WorkingTree.pull.
1110
    def pull(self, source, overwrite=False, stop_revision=None,
1111
             change_reporter=None, possible_transports=None, local=False,
1112
             show_base=False):
1113
        with self.lock_write(), source.lock_read():
1114
            old_revision = self.branch.last_revision()
1115
            basis_tree = self.basis_tree()
1116
            count = self.branch.pull(source, overwrite, stop_revision,
1117
                                     possible_transports=possible_transports,
1118
                                     local=local)
1119
            new_revision = self.branch.last_revision()
1120
            if new_revision != old_revision:
1121
                with basis_tree.lock_read():
1122
                    new_basis_tree = self.branch.basis_tree()
1123
                    merge.merge_inner(
1124
                                self.branch,
1125
                                new_basis_tree,
1126
                                basis_tree,
1127
                                this_tree=self,
1128
                                change_reporter=change_reporter,
1129
                                show_base=show_base)
1130
            return count
1131
0.200.1308 by Jelmer Vernooij
Write index to disk after adding files.
1132
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
1133
class GitWorkingTreeFormat(workingtree.WorkingTreeFormat):
1134
0.200.1206 by Jelmer Vernooij
Implement GitWorkingTree.all_file_ids.
1135
    _tree_class = GitWorkingTree
1136
0.200.1295 by Jelmer Vernooij
Mark working trees as not supporting directories.
1137
    supports_versioned_directories = False
1138
0.200.1661 by Jelmer Vernooij
Set supports_setting_file_ids to False.
1139
    supports_setting_file_ids = False
1140
0.200.1677 by Jelmer Vernooij
Mark shelving as unsupported.
1141
    supports_store_uncommitted = False
1142
0.200.1723 by Jelmer Vernooij
Set supports_leftmost_parent_id_as_ghost property.
1143
    supports_leftmost_parent_id_as_ghost = False
1144
0.200.1779 by Jelmer Vernooij
Actually store git revision ids rather than bzr revision ids.
1145
    supports_righthand_parent_id_as_ghost = False
1146
0.200.1768 by Jelmer Vernooij
Fix handling of normalized filenames.
1147
    requires_normalized_unicode_filenames = True
1148
0.367.1 by Jelmer Vernooij
Set supports_merge_modified flag.
1149
    supports_merge_modified = False
1150
0.200.656 by Jelmer Vernooij
Implement GitWorkingTreeFormat._matchingbzrdir.
1151
    @property
0.200.1665 by Jelmer Vernooij
Rename _matchingbzrdir to _matchingcnotroldir.
1152
    def _matchingcontroldir(self):
0.200.1641 by Jelmer Vernooij
Use relative imports where possible.
1153
        from .dir import LocalGitControlDirFormat
0.200.1013 by Jelmer Vernooij
More renames.
1154
        return LocalGitControlDirFormat()
0.200.656 by Jelmer Vernooij
Implement GitWorkingTreeFormat._matchingbzrdir.
1155
0.200.90 by Jelmer Vernooij
Basic support for opening working trees.
1156
    def get_format_description(self):
1157
        return "Git Working Tree"
0.200.616 by Jelmer Vernooij
Provide custom intertree implementation for GitRevisionTree->GitWorkingTree.
1158
0.200.1648 by Jelmer Vernooij
Fix compatibility with newer versions of breezy.
1159
    def initialize(self, a_controldir, revision_id=None, from_branch=None,
0.200.1096 by Jelmer Vernooij
Implement GitWorkingTreeFormat.initialize.
1160
                   accelerator_tree=None, hardlink=False):
1161
        """See WorkingTreeFormat.initialize()."""
0.200.1648 by Jelmer Vernooij
Fix compatibility with newer versions of breezy.
1162
        if not isinstance(a_controldir, LocalGitDir):
1163
            raise errors.IncompatibleFormat(self, a_controldir)
1164
        index = Index(a_controldir.root_transport.local_abspath(".git/index"))
0.200.1096 by Jelmer Vernooij
Implement GitWorkingTreeFormat.initialize.
1165
        index.write()
0.388.2 by Jelmer Vernooij
Allow opening nascent branches when opening trees.
1166
        branch = a_controldir.open_branch(nascent_ok=True)
0.308.1 by Jelmer Vernooij
Set revision id if specified to WorkingTreeFormat.initialize.
1167
        if revision_id is not None:
1168
            branch.set_last_revision(revision_id)
0.200.1680 by Jelmer Vernooij
Fix repo locks.
1169
        wt = GitWorkingTree(
0.308.1 by Jelmer Vernooij
Set revision id if specified to WorkingTreeFormat.initialize.
1170
                a_controldir, a_controldir.open_repository(), branch, index)
0.200.1680 by Jelmer Vernooij
Fix repo locks.
1171
        for hook in MutableTree.hooks['post_build_tree']:
1172
            hook(wt)
1173
        return wt
0.200.1096 by Jelmer Vernooij
Implement GitWorkingTreeFormat.initialize.
1174
0.200.616 by Jelmer Vernooij
Provide custom intertree implementation for GitRevisionTree->GitWorkingTree.
1175
0.287.3 by Jelmer Vernooij
Some improvements to changes iterator.
1176
class InterIndexGitTree(InterGitTrees):
0.200.616 by Jelmer Vernooij
Provide custom intertree implementation for GitRevisionTree->GitWorkingTree.
1177
    """InterTree that works between a Git revision tree and an index."""
1178
1179
    def __init__(self, source, target):
1180
        super(InterIndexGitTree, self).__init__(source, target)
1181
        self._index = target.index
1182
1183
    @classmethod
1184
    def is_compatible(cls, source, target):
0.200.1641 by Jelmer Vernooij
Use relative imports where possible.
1185
        from .repository import GitRevisionTree
0.200.1636 by Jelmer Vernooij
Some formatting fixes.
1186
        return (isinstance(source, GitRevisionTree) and
0.200.616 by Jelmer Vernooij
Provide custom intertree implementation for GitRevisionTree->GitWorkingTree.
1187
                isinstance(target, GitWorkingTree))
1188
0.287.3 by Jelmer Vernooij
Some improvements to changes iterator.
1189
    def _iter_git_changes(self, want_unchanged=False, specific_files=None,
0.391.4 by Jelmer Vernooij
Simplify InterGitTrees implementation.
1190
            require_versioned=False, extra_trees=None,
0.391.3 by Jelmer Vernooij
Simplify handling of unversioned paths.
1191
            want_unversioned=False):
0.385.1 by Jelmer Vernooij
Use specific_files argument to Tree.iter_entries_by_dir.
1192
        trees = [self.source]
1193
        if extra_trees is not None:
1194
            trees.extend(extra_trees)
1195
        if specific_files is not None:
1196
            specific_files = self.target.find_related_paths_across_trees(
1197
                    specific_files, trees,
1198
                    require_versioned=require_versioned)
0.287.3 by Jelmer Vernooij
Some improvements to changes iterator.
1199
        # TODO(jelmer): Restrict to specific_files, for performance reasons.
1200
        with self.lock_read():
0.200.1754 by Jelmer Vernooij
Avoid updating index when just comparing trees.
1201
            return changes_between_git_tree_and_working_copy(
0.287.3 by Jelmer Vernooij
Some improvements to changes iterator.
1202
                self.source.store, self.source.tree,
0.357.1 by Jelmer Vernooij
Fix some remove tests.
1203
                self.target, want_unchanged=want_unchanged,
0.391.3 by Jelmer Vernooij
Simplify handling of unversioned paths.
1204
                want_unversioned=want_unversioned)
0.287.3 by Jelmer Vernooij
Some improvements to changes iterator.
1205
0.200.1179 by Jelmer Vernooij
Avoid using verifiers for natively imported revisions, save a lot of time.
1206
0.200.616 by Jelmer Vernooij
Provide custom intertree implementation for GitRevisionTree->GitWorkingTree.
1207
tree.InterTree.register_optimiser(InterIndexGitTree)
0.200.1529 by Jelmer Vernooij
Add changes_between_tree_and_index.
1208
1209
0.200.1754 by Jelmer Vernooij
Avoid updating index when just comparing trees.
1210
def changes_between_git_tree_and_working_copy(store, from_tree_sha, target,
0.391.7 by Jelmer Vernooij
Fix reporting of missing files in .iter_changes.
1211
        want_unchanged=False, want_unversioned=False):
0.200.1754 by Jelmer Vernooij
Avoid updating index when just comparing trees.
1212
    """Determine the changes between a git tree and a working tree with index.
1213
1214
    """
0.391.7 by Jelmer Vernooij
Fix reporting of missing files in .iter_changes.
1215
    extras = set()
1216
    blobs = {}
1217
    # Report dirified directories to commit_tree first, so that they can be
1218
    # replaced with non-empty directories if they have contents.
1219
    dirified = []
0.391.6 by Jelmer Vernooij
Directly call index_entry_from_path.
1220
    target_root_path = target.abspath('.').encode(sys.getfilesystemencoding())
1221
    for path, index_entry in target.index.iteritems():
1222
        try:
1223
            live_entry = index_entry_from_path(
1224
                    target.abspath(path.decode('utf-8')).encode(osutils._fs_enc))
1225
        except EnvironmentError as e:
0.391.7 by Jelmer Vernooij
Fix reporting of missing files in .iter_changes.
1226
            if e.errno == errno.ENOENT:
1227
                # Entry was removed; keep it listed, but mark it as gone.
1228
                blobs[path] = (ZERO_SHA, 0)
1229
            elif e.errno == errno.EISDIR:
1230
                # Entry was turned into a directory
1231
                dirified.append((path, Tree().id, stat.S_IFDIR))
1232
                store.add_object(Tree())
0.391.6 by Jelmer Vernooij
Directly call index_entry_from_path.
1233
            else:
1234
                raise
0.391.1 by Jelmer Vernooij
Use new iter_fresh_objects.
1235
        else:
0.391.7 by Jelmer Vernooij
Fix reporting of missing files in .iter_changes.
1236
            blobs[path] = (live_entry.sha, cleanup_mode(live_entry.mode))
0.391.3 by Jelmer Vernooij
Simplify handling of unversioned paths.
1237
    if want_unversioned:
1238
        for e in target.extras():
1239
            ap = target.abspath(e)
1240
            st = os.lstat(ap)
1241
            try:
1242
                np, accessible = osutils.normalized_filename(e)
1243
            except UnicodeDecodeError:
1244
                raise errors.BadFilenameEncoding(
1245
                    e, osutils._fs_enc)
1246
            if stat.S_ISDIR(st.st_mode):
1247
                blob = Tree()
1248
            else:
1249
                blob = blob_from_path_and_stat(ap.encode('utf-8'), st)
1250
            store.add_object(blob)
1251
            np = np.encode('utf-8')
0.392.1 by Jelmer Vernooij
Call cleanup mode for extra entries.
1252
            blobs[np] = (blob.id, cleanup_mode(st.st_mode))
0.391.7 by Jelmer Vernooij
Fix reporting of missing files in .iter_changes.
1253
            extras.add(np)
1254
    to_tree_sha = commit_tree(store, dirified + [(p, s, m) for (p, (s, m)) in blobs.iteritems()])
1255
    return store.tree_changes(
0.391.2 by Jelmer Vernooij
Pass along target_missing.
1256
        from_tree_sha, to_tree_sha, include_trees=True,
0.391.7 by Jelmer Vernooij
Fix reporting of missing files in .iter_changes.
1257
        want_unchanged=want_unchanged, change_type_same=True), extras