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