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