100
IGNORE_FILENAME = ".gitignore"
103
class GitWorkingTree(MutableGitIndexTree,workingtree.WorkingTree):
87
class GitWorkingTree(MutableGitIndexTree, workingtree.WorkingTree):
104
88
"""A Git working tree."""
106
90
def __init__(self, controldir, repo, branch):
112
96
self.store = self.repository._git.object_store
113
97
self.mapping = self.repository.get_mapping()
114
98
self._branch = branch
115
self._transport = controldir.transport
99
self._transport = self.repository._git._controltransport
116
100
self._format = GitWorkingTreeFormat()
117
101
self.index = None
118
102
self._index_file = None
150
134
self._lock_mode = 'w'
151
135
self._lock_count = 1
153
self._index_file = GitFile(self.control_transport.local_abspath('index'), 'wb')
137
self._index_file = GitFile(
138
self.control_transport.local_abspath('index'), 'wb')
154
139
except FileLocked:
155
140
raise errors.LockContention('index')
156
141
self._read_index()
157
142
elif self._lock_mode == 'r':
158
143
raise errors.ReadOnlyError(self)
145
self._lock_count += 1
162
147
def lock_tree_write(self):
163
148
self.branch.lock_read()
165
150
self._lock_write_tree()
166
151
return lock.LogicalLockResult(self.unlock)
152
except BaseException:
168
153
self.branch.unlock()
236
221
def _set_merges_from_parent_ids(self, rhs_parent_ids):
238
merges = [self.branch.lookup_bzr_revision_id(revid)[0] for revid in rhs_parent_ids]
223
merges = [self.branch.lookup_bzr_revision_id(
224
revid)[0] for revid in rhs_parent_ids]
239
225
except errors.NoSuchRevision as e:
240
226
raise errors.GhostRevisionUnusableHere(e.revision)
242
self.control_transport.put_bytes('MERGE_HEAD', b'\n'.join(merges),
228
self.control_transport.put_bytes(
229
'MERGE_HEAD', b'\n'.join(merges),
243
230
mode=self.controldir._get_file_mode())
260
247
working tree. Any of these may be ghosts.
262
249
with self.lock_tree_write():
263
self._check_parents_for_ghosts(revision_ids,
264
allow_leftmost_as_ghost=allow_leftmost_as_ghost)
250
self._check_parents_for_ghosts(
251
revision_ids, allow_leftmost_as_ghost=allow_leftmost_as_ghost)
265
252
for revision_id in revision_ids:
266
253
_mod_revision.check_not_reserved_id(revision_id)
302
290
def remove(self, files, verbose=False, to_file=None, keep_files=True,
304
292
"""Remove nominated files from the working tree metadata.
306
294
:param files: File paths relative to the basedir.
317
305
def backup(file_to_backup):
318
306
abs_path = self.abspath(file_to_backup)
319
backup_name = self.controldir._available_backup_name(file_to_backup)
307
backup_name = self.controldir._available_backup_name(
320
309
osutils.rename(abs_path, self.abspath(backup_name))
321
310
return "removed %s (but kept a copy: %s)" % (
322
311
file_to_backup, backup_name)
353
342
files = list(all_files)
355
344
if len(files) == 0:
356
return # nothing to do
345
return # nothing to do
358
# Sort needed to first handle directory content before the directory
347
# Sort needed to first handle directory content before the
359
349
files.sort(reverse=True)
361
351
# Bail out if we are going to delete files we shouldn't
362
352
if not keep_files and not force:
363
353
for (file_id, path, content_change, versioned, parent_id, name,
364
kind, executable) in self.iter_changes(self.basis_tree(),
365
include_unchanged=True, require_versioned=False,
366
want_unversioned=True, specific_files=files):
367
if versioned[0] == False:
354
kind, executable) in self.iter_changes(
355
self.basis_tree(), include_unchanged=True,
356
require_versioned=False, want_unversioned=True,
357
specific_files=files):
358
if versioned[0] is False:
368
359
# The record is unknown or newly added
369
360
files_to_backup.append(path[1])
370
files_to_backup.extend(osutils.parent_directories(path[1]))
371
elif (content_change and (kind[1] is not None) and
372
osutils.is_inside_any(files, path[1])):
361
files_to_backup.extend(
362
osutils.parent_directories(path[1]))
363
elif (content_change and (kind[1] is not None)
364
and osutils.is_inside_any(files, path[1])):
373
365
# Versioned and changed, but not deleted, and still
374
366
# in one of the dirs to be deleted.
375
367
files_to_backup.append(path[1])
376
files_to_backup.extend(osutils.parent_directories(path[1]))
368
files_to_backup.extend(
369
osutils.parent_directories(path[1]))
421
414
# expand any symlinks in the directory part, while leaving the
423
416
# only expanding if symlinks are supported avoids windows path bugs
424
if osutils.has_symlinks():
417
if self.supports_symlinks():
425
418
file_list = list(map(osutils.normalizepath, file_list))
427
420
conflicts_related = set()
441
437
raise workingtree.SettingFileIdUnsupported()
443
439
with self.lock_tree_write():
444
for filepath in osutils.canonical_relpaths(self.basedir, file_list):
440
for filepath in osutils.canonical_relpaths(
441
self.basedir, file_list):
445
442
filepath, can_access = osutils.normalized_filename(filepath)
446
443
if not can_access:
447
444
raise errors.InvalidNormalization(filepath)
449
446
abspath = self.abspath(filepath)
450
447
kind = osutils.file_kind(abspath)
451
448
if kind in ("file", "symlink"):
452
(index, subpath) = self._lookup_index(filepath.encode('utf-8'))
449
(index, subpath) = self._lookup_index(
450
filepath.encode('utf-8'))
453
451
if subpath in index:
454
452
# Already present
458
456
self._index_add_entry(filepath, kind)
459
457
added.append(filepath)
460
458
elif kind == "directory":
461
(index, subpath) = self._lookup_index(filepath.encode('utf-8'))
459
(index, subpath) = self._lookup_index(
460
filepath.encode('utf-8'))
462
461
if subpath not in index:
463
462
call_action(filepath, kind)
469
468
abs_user_dir = self.abspath(user_dir)
470
469
if user_dir != '':
472
transport = _mod_transport.get_transport_from_path(abs_user_dir)
471
transport = _mod_transport.get_transport_from_path(
473
473
_mod_controldir.ControlDirFormat.find_format(transport)
475
475
except errors.NotBranchError:
485
485
for name in os.listdir(abs_user_dir):
486
486
subp = os.path.join(user_dir, name)
487
if self.is_control_filename(subp) or self.mapping.is_special_file(subp):
487
if (self.is_control_filename(subp) or
488
self.mapping.is_special_file(subp)):
489
490
ignore_glob = self.is_ignored(subp)
490
491
if ignore_glob is not None:
495
496
if kind == "directory":
496
497
user_dirs.append(subp)
498
(index, subpath) = self._lookup_index(subp.encode('utf-8'))
499
(index, subpath) = self._lookup_index(
500
subp.encode('utf-8'))
499
501
if subpath in index:
500
502
# Already present
502
504
if subp in conflicts_related:
504
call_action(filepath, kind)
506
call_action(subp, kind)
506
508
self._index_add_entry(subp, kind)
507
509
added.append(subp)
513
515
def _iter_files_recursive(self, from_dir=None, include_dirs=False):
514
516
if from_dir is None:
516
for (dirpath, dirnames, filenames) in os.walk(self.abspath(from_dir).encode(osutils._fs_enc)):
518
encoded_from_dir = self.abspath(from_dir).encode(osutils._fs_enc)
519
for (dirpath, dirnames, filenames) in os.walk(encoded_from_dir):
517
520
dir_relpath = dirpath[len(self.basedir):].strip(b"/")
518
if self.controldir.is_control_filename(dir_relpath.decode(osutils._fs_enc)):
521
if self.controldir.is_control_filename(
522
dir_relpath.decode(osutils._fs_enc)):
520
524
for name in list(dirnames):
521
if self.controldir.is_control_filename(name.decode(osutils._fs_enc)):
525
if self.controldir.is_control_filename(
526
name.decode(osutils._fs_enc)):
522
527
dirnames.remove(name)
524
529
relpath = os.path.join(dir_relpath, name)
528
533
except UnicodeDecodeError:
529
534
raise errors.BadFilenameEncoding(
530
535
relpath, osutils._fs_enc)
531
if not self._has_dir(relpath):
532
dirnames.remove(name)
536
if not self._has_dir(relpath):
537
dirnames.remove(name)
533
538
for name in filenames:
534
if not self.mapping.is_special_file(name):
535
yp = os.path.join(dir_relpath, name)
537
yield yp.decode(osutils._fs_enc)
538
except UnicodeDecodeError:
539
raise errors.BadFilenameEncoding(
539
if self.mapping.is_special_file(name):
541
if self.controldir.is_control_filename(
542
name.decode(osutils._fs_enc, 'replace')):
544
yp = os.path.join(dir_relpath, name)
546
yield yp.decode(osutils._fs_enc)
547
except UnicodeDecodeError:
548
raise errors.BadFilenameEncoding(
542
551
def extras(self):
543
552
"""Yield all unversioned files in this WorkingTree.
545
554
with self.lock_read():
546
index_paths = set([p.decode('utf-8') for p, i in self._recurse_index_entries()])
547
all_paths = set(self._iter_files_recursive(include_dirs=True))
548
for p in (all_paths - index_paths):
549
if not self._has_dir(p.encode('utf-8')):
556
[p.decode('utf-8') for p, i in self._recurse_index_entries()])
557
all_paths = set(self._iter_files_recursive(include_dirs=False))
558
return iter(all_paths - index_paths)
552
560
def _gather_kinds(self, files, kinds):
553
561
"""See MutableTree._gather_kinds."""
556
564
if kinds[pos] is None:
557
565
fullpath = osutils.normpath(self.abspath(f))
559
kind = osutils.file_kind(fullpath)
567
kind = osutils.file_kind(fullpath)
560
568
except OSError as e:
561
569
if e.errno == errno.ENOENT:
562
570
raise errors.NoSuchFile(fullpath)
563
if kind == 'directory' and f != '' and os.path.exists(os.path.join(fullpath, '.git')):
571
if (kind == 'directory' and f != '' and
572
os.path.exists(os.path.join(fullpath, '.git'))):
564
573
kind = 'tree-reference'
565
574
kinds[pos] = kind
568
577
if self._lock_mode != 'w':
569
578
raise errors.NotWriteLocked(self)
570
579
# TODO(jelmer): This shouldn't be writing in-place, but index.lock is
571
# already in use and GitFile doesn't allow overriding the lock file name :(
580
# already in use and GitFile doesn't allow overriding the lock file
572
582
f = open(self.control_transport.local_abspath('index'), 'wb')
573
583
# Note that _flush will close the file
605
tree_lookup_path(self.store.__getitem__, root_tree, path.encode('utf-8'))
615
tree_lookup_path(self.store.__getitem__,
616
root_tree, path.encode('utf-8'))
611
def get_file_mtime(self, path, file_id=None):
622
def get_file_mtime(self, path):
612
623
"""See Tree.get_file_mtime."""
614
625
return self._lstat(path).st_mtime
627
638
ignore_globs = set()
628
639
ignore_globs.update(ignores.get_runtime_ignores())
629
640
ignore_globs.update(ignores.get_user_ignores())
630
self._global_ignoreglobster = globbing.ExceptionGlobster(ignore_globs)
641
self._global_ignoreglobster = globbing.ExceptionGlobster(
631
643
match = self._global_ignoreglobster.match(filename)
632
644
if match is not None:
677
689
self.store.__getitem__, self.store[head].tree)
678
690
self._fileid_map = self._basis_fileid_map.copy()
680
def get_file_verifier(self, path, file_id=None, stat_value=None):
692
def get_file_verifier(self, path, stat_value=None):
681
693
with self.lock_read():
682
694
(index, subpath) = self._lookup_index(path.encode('utf-8'))
687
699
return ("GIT", None)
688
700
raise errors.NoSuchFile(path)
690
def get_file_sha1(self, path, file_id=None, stat_value=None):
702
def get_file_sha1(self, path, stat_value=None):
691
703
with self.lock_read():
692
704
if not self.is_versioned(path):
693
705
raise errors.NoSuchFile(path)
709
721
def _is_executable_from_path_and_stat_from_basis(self, path, stat_result):
710
722
return self.basis_tree().is_executable(path)
712
def stored_kind(self, path, file_id=None):
724
def stored_kind(self, path):
713
725
with self.lock_read():
714
726
encoded_path = path.encode('utf-8')
715
727
(index, subpath) = self._lookup_index(encoded_path)
725
737
return os.lstat(self.abspath(path))
727
739
def _live_entry(self, path):
728
return index_entry_from_path(self.abspath(path.decode('utf-8')).encode(osutils._fs_enc))
740
encoded_path = self.abspath(path.decode('utf-8')).encode(
742
return index_entry_from_path(encoded_path)
730
def is_executable(self, path, file_id=None):
744
def is_executable(self, path):
731
745
with self.lock_read():
732
746
if self._supports_executable():
733
747
mode = self._lstat(path).st_mode
743
757
if self._supports_executable():
744
758
return self._is_executable_from_path_and_stat_from_stat(path, stat_result)
746
return self._is_executable_from_path_and_stat_from_basis(path, stat_result)
760
return self._is_executable_from_path_and_stat_from_basis(
748
763
def list_files(self, include_root=False, from_dir=None, recursive=True):
764
if from_dir is None or from_dir == '.':
752
767
fk_entries = {'directory': tree.TreeDirectory,
756
771
with self.lock_read():
757
772
root_ie = self._get_dir_ie(u"", None)
758
773
if include_root and not from_dir:
759
yield "", "V", root_ie.kind, root_ie.file_id, root_ie
774
yield "", "V", root_ie.kind, root_ie
760
775
dir_ids[u""] = root_ie.file_id
762
path_iterator = sorted(self._iter_files_recursive(from_dir, include_dirs=True))
777
path_iterator = sorted(
778
self._iter_files_recursive(from_dir, include_dirs=True))
764
path_iterator = sorted([os.path.join(from_dir, name.decode(osutils._fs_enc)) for name in
765
os.listdir(self.abspath(from_dir).encode(osutils._fs_enc))
766
if not self.controldir.is_control_filename(name.decode(osutils._fs_enc))
767
and not self.mapping.is_special_file(name.decode(osutils._fs_enc))])
780
encoded_from_dir = self.abspath(from_dir).encode(
782
path_iterator = sorted(
783
[os.path.join(from_dir, name.decode(osutils._fs_enc))
784
for name in os.listdir(encoded_from_dir)
785
if not self.controldir.is_control_filename(
786
name.decode(osutils._fs_enc)) and
787
not self.mapping.is_special_file(
788
name.decode(osutils._fs_enc))])
768
789
for path in path_iterator:
770
791
encoded_path = path.encode("utf-8")
779
800
kind = self.kind(path)
780
801
parent, name = posixpath.split(path)
781
for dir_path, dir_ie in self._add_missing_parent_ids(parent, dir_ids):
802
for dir_path, dir_ie in self._add_missing_parent_ids(
783
805
if kind in ('directory', 'tree-reference'):
784
806
if path != from_dir:
785
807
if self._has_dir(encoded_path):
786
808
ie = self._get_dir_ie(path, self.path2id(path))
789
810
elif self.is_ignored(path):
791
812
ie = fk_entries[kind]()
795
815
ie = fk_entries[kind]()
797
yield posixpath.relpath(path, from_dir), status, kind, file_id, ie
816
yield (posixpath.relpath(path, from_dir), status, kind,
799
819
if value is not None:
800
820
ie = self._get_file_ie(name, path, value, dir_ids[parent])
801
yield posixpath.relpath(path, from_dir), "V", ie.kind, ie.file_id, ie
821
yield (posixpath.relpath(path, from_dir), "V", ie.kind, ie)
803
823
ie = fk_entries[kind]()
804
yield posixpath.relpath(path, from_dir), ("I" if self.is_ignored(path) else "?"), kind, None, ie
824
yield (posixpath.relpath(path, from_dir),
825
("I" if self.is_ignored(path) else "?"), kind, ie)
806
827
def all_file_ids(self):
807
with self.lock_read():
808
ids = {u"": self.path2id("")}
809
for path in self.index:
810
if self.mapping.is_special_file(path):
812
path = path.decode("utf-8")
813
parent = posixpath.dirname(path).strip("/")
814
for e in self._add_missing_parent_ids(parent, ids):
816
ids[path] = self.path2id(path)
817
return set(ids.values())
828
raise errors.UnsupportedOperation(self.all_file_ids, self)
819
830
def all_versioned_paths(self):
820
831
with self.lock_read():
834
def iter_child_entries(self, path, file_id=None):
845
def iter_child_entries(self, path):
835
846
encoded_path = path.encode('utf-8')
836
847
with self.lock_read():
837
848
parent_id = self.path2id(path)
838
849
found_any = False
839
seen_children = set()
840
850
for item_path, value in self.index.iteritems():
841
851
decoded_item_path = item_path.decode('utf-8')
842
852
if self.mapping.is_special_file(item_path):
847
857
subpath = posixpath.relpath(decoded_item_path, path)
848
858
if '/' in subpath:
849
859
dirname = subpath.split('/', 1)[0]
850
file_ie = self._get_dir_ie(posixpath.join(path, dirname), parent_id)
860
file_ie = self._get_dir_ie(
861
posixpath.join(path, dirname), parent_id)
852
863
(unused_parent, name) = posixpath.split(decoded_item_path)
853
864
file_ie = self._get_file_ie(
861
872
conflicts = _mod_conflicts.ConflictList()
862
873
for item_path, value in self.index.iteritems():
863
874
if value.flags & FLAG_STAGEMASK:
864
conflicts.append(_mod_conflicts.TextConflict(item_path.decode('utf-8')))
875
conflicts.append(_mod_conflicts.TextConflict(
876
item_path.decode('utf-8')))
867
879
def set_conflicts(self, conflicts):
883
895
self.index[path] = (value[:9] + (value[9] | FLAG_STAGEMASK, ))
885
self.index[path] = (value[:9] + (value[9] &~ FLAG_STAGEMASK, ))
897
self.index[path] = (value[:9] + (value[9] & ~ FLAG_STAGEMASK, ))
887
899
def add_conflicts(self, new_conflicts):
888
900
with self.lock_tree_write():
889
901
for conflict in new_conflicts:
890
if conflict.typestring in ('text conflict', 'contents conflict'):
902
if conflict.typestring in ('text conflict',
903
'contents conflict'):
892
self._set_conflicted(conflict.path.encode('utf-8'), True)
905
self._set_conflicted(
906
conflict.path.encode('utf-8'), True)
894
raise errors.UnsupportedOperation(self.add_conflicts, self)
908
raise errors.UnsupportedOperation(
909
self.add_conflicts, self)
896
911
raise errors.UnsupportedOperation(self.add_conflicts, self)
921
936
current_disk = next(disk_iterator)
922
937
disk_finished = False
923
938
except OSError as e:
924
if not (e.errno == errno.ENOENT or
925
(sys.platform == 'win32' and e.errno == ERROR_PATH_NOT_FOUND)):
939
if not (e.errno == errno.ENOENT
940
or (sys.platform == 'win32' and e.errno == ERROR_PATH_NOT_FOUND)):
927
942
current_disk = None
928
943
disk_finished = True
941
956
cur_disk_dir_content) = ((None, None), None)
942
957
if not disk_finished:
943
958
# strip out .bzr dirs
944
if (cur_disk_dir_path_from_top[top_strip_len:] == '' and
945
len(cur_disk_dir_content) > 0):
959
if (cur_disk_dir_path_from_top[top_strip_len:] == ''
960
and len(cur_disk_dir_content) > 0):
946
961
# osutils.walkdirs can be made nicer -
947
962
# yield the path-from-prefix rather than the pathjoined
949
964
bzrdir_loc = bisect_left(cur_disk_dir_content,
951
if (bzrdir_loc < len(cur_disk_dir_content)
952
and self.controldir.is_control_filename(
966
if (bzrdir_loc < len(cur_disk_dir_content) and
967
self.controldir.is_control_filename(
953
968
cur_disk_dir_content[bzrdir_loc][0])):
954
969
# we dont yield the contents of, or, .bzr itself.
955
970
del cur_disk_dir_content[bzrdir_loc]
960
975
# everything is missing
963
direction = ((current_inv[0][0] > cur_disk_dir_relpath) -
964
(current_inv[0][0] < cur_disk_dir_relpath))
978
direction = ((current_inv[0][0] > cur_disk_dir_relpath)
979
- (current_inv[0][0] < cur_disk_dir_relpath))
965
980
if direction > 0:
966
981
# disk is before inventory - unknown
967
982
dirblock = [(relpath, basename, kind, stat, None, None) for
968
relpath, basename, kind, stat, top_path in
969
cur_disk_dir_content]
983
relpath, basename, kind, stat, top_path in
984
cur_disk_dir_content]
970
985
yield (cur_disk_dir_relpath, None), dirblock
972
987
current_disk = next(disk_iterator)
975
990
elif direction < 0:
976
991
# inventory is before disk - missing.
977
992
dirblock = [(relpath, basename, 'unknown', None, fileid, kind)
978
for relpath, basename, dkind, stat, fileid, kind in
993
for relpath, basename, dkind, stat, fileid, kind in
980
995
yield (current_inv[0][0], current_inv[0][1]), dirblock
982
997
current_inv = next(inventory_iterator)
987
1002
# merge the inventory and disk data together
989
1004
for relpath, subiterator in itertools.groupby(sorted(
990
current_inv[1] + cur_disk_dir_content,
991
key=operator.itemgetter(0)), operator.itemgetter(1)):
1005
current_inv[1] + cur_disk_dir_content,
1006
key=operator.itemgetter(0)), operator.itemgetter(1)):
992
1007
path_elements = list(subiterator)
993
1008
if len(path_elements) == 2:
994
1009
inv_row, disk_row = path_elements
995
1010
# versioned, present file
996
1011
dirblock.append((inv_row[0],
997
inv_row[1], disk_row[2],
998
disk_row[3], inv_row[4],
1012
inv_row[1], disk_row[2],
1013
disk_row[3], inv_row[4],
1000
1015
elif len(path_elements[0]) == 5:
1001
1016
# unknown disk file
1002
dirblock.append((path_elements[0][0],
1003
path_elements[0][1], path_elements[0][2],
1004
path_elements[0][3], None, None))
1018
(path_elements[0][0], path_elements[0][1],
1019
path_elements[0][2], path_elements[0][3],
1005
1021
elif len(path_elements[0]) == 6:
1006
1022
# versioned, absent file.
1007
dirblock.append((path_elements[0][0],
1008
path_elements[0][1], 'unknown', None,
1009
path_elements[0][4], path_elements[0][5]))
1024
(path_elements[0][0], path_elements[0][1],
1025
'unknown', None, path_elements[0][4],
1026
path_elements[0][5]))
1011
1028
raise NotImplementedError('unreachable code')
1012
1029
yield current_inv[0], dirblock
1026
1043
per_dir = defaultdict(set)
1027
1044
if prefix == b"":
1028
1045
per_dir[(u'', self.get_root_id())] = set()
1029
1047
def add_entry(path, kind):
1030
1048
if path == b'' or not path.startswith(prefix):
1037
1055
raise ValueError(value)
1038
1056
per_dir[(dirname, dir_file_id)].add(
1039
1057
(path.decode("utf-8"), child_name.decode("utf-8"),
1041
self.path2id(path.decode("utf-8")),
1059
self.path2id(path.decode("utf-8")),
1043
1061
with self.lock_read():
1044
1062
for path, value in self.index.iteritems():
1045
1063
if self.mapping.is_special_file(path):
1058
1076
def apply_inventory_delta(self, changes):
1059
1077
for (old_path, new_path, file_id, ie) in changes:
1060
1078
if old_path is not None:
1061
(index, old_subpath) = self._lookup_index(old_path.encode('utf-8'))
1079
(index, old_subpath) = self._lookup_index(
1080
old_path.encode('utf-8'))
1063
1082
self._index_del_entry(index, old_subpath)
1064
1083
except KeyError:
1068
1087
if new_path is not None and ie.kind != 'directory':
1069
1088
if ie.kind == 'tree-reference':
1070
1089
self._index_add_entry(
1072
reference_revision=ie.reference_revision)
1091
reference_revision=ie.reference_revision)
1074
1093
self._index_add_entry(new_path, ie.kind)
1077
def annotate_iter(self, path, file_id=None,
1096
def annotate_iter(self, path,
1078
1097
default_revision=_mod_revision.CURRENT_REVISION):
1079
1098
"""See Tree.annotate_iter
1092
1111
parent_tree = self.revision_tree(parent_id)
1093
1112
except errors.NoSuchRevisionInTree:
1094
1113
parent_tree = self.branch.repository.revision_tree(
1096
1115
with parent_tree.lock_read():
1097
# TODO(jelmer): Use rename/copy tracker to find path name in parent
1116
# TODO(jelmer): Use rename/copy tracker to find path name
1098
1118
parent_path = path
1100
1120
kind = parent_tree.kind(parent_path)
1101
1121
except errors.NoSuchFile:
1103
1123
if kind != 'file':
1104
# Note: this is slightly unnecessary, because symlinks and
1105
# directories have a "text" which is the empty text, and we
1106
# know that won't mess up annotations. But it seems cleaner
1124
# Note: this is slightly unnecessary, because symlinks
1125
# and directories have a "text" which is the empty
1126
# text, and we know that won't mess up annotations. But
1108
1129
parent_text_key = (
1140
1161
self.user_transport.local_abspath('.'),
1141
1162
self.control_transport.local_abspath("index"),
1143
None if self.branch.head is None else self.store[self.branch.head].tree,
1165
if self.branch.head is None
1166
else self.store[self.branch.head].tree,
1144
1167
honor_filemode=self._supports_executable())
1146
1169
def reset_state(self, revision_ids=None):
1155
1178
self.index.clear()
1156
1179
self._index_dirty = True
1157
1180
if self.branch.head is not None:
1158
for entry in self.store.iter_tree_contents(self.store[self.branch.head].tree):
1181
for entry in self.store.iter_tree_contents(
1182
self.store[self.branch.head].tree):
1159
1183
if not validate_path(entry.path):
1162
1186
if S_ISGITLINK(entry.mode):
1163
pass # TODO(jelmer): record and return submodule paths
1187
pass # TODO(jelmer): record and return submodule paths
1165
1189
# Let's at least try to use the working tree file:
1167
st = self._lstat(self.abspath(entry.path.decode('utf-8')))
1191
st = self._lstat(self.abspath(
1192
entry.path.decode('utf-8')))
1168
1193
except OSError:
1169
1194
# But if it doesn't exist, we'll make something up.
1170
1195
obj = self.store[entry.sha]
1171
1196
st = os.stat_result((entry.mode, 0, 0, 0,
1172
0, 0, len(obj.as_raw_string()), 0,
1198
obj.as_raw_string()), 0,
1174
1200
(index, subpath) = self._lookup_index(entry.path)
1175
1201
index[subpath] = index_entry_from_stat(st, entry.sha, 0)
1188
1214
with basis_tree.lock_read():
1189
1215
new_basis_tree = self.branch.basis_tree()
1190
1216
merge.merge_inner(
1195
change_reporter=change_reporter,
1196
show_base=show_base)
1221
change_reporter=change_reporter,
1222
show_base=show_base)
1199
1225
def add_reference(self, sub_tree):
1206
1232
sub_tree_path = self.relpath(sub_tree.basedir)
1207
1233
except errors.PathNotChild:
1208
1234
raise BadReferenceTarget(
1209
self, sub_tree, 'Target not inside tree.')
1235
self, sub_tree, 'Target not inside tree.')
1211
1237
self._add([sub_tree_path], [None], ['tree-reference'])
1213
1239
def _read_submodule_head(self, path):
1214
1240
return read_submodule_head(self.abspath(path))
1216
def get_reference_revision(self, path, file_id=None):
1242
def get_reference_revision(self, path):
1217
1243
hexsha = self._read_submodule_head(path)
1218
1244
if hexsha is None:
1219
1245
return _mod_revision.NULL_REVISION
1220
1246
return self.branch.lookup_foreign_revision_id(hexsha)
1222
def get_nested_tree(self, path, file_id=None):
1248
def get_nested_tree(self, path):
1223
1249
return workingtree.WorkingTree.open(self.abspath(path))
1225
1251
def _directory_is_tree_reference(self, relpath):
1227
1253
# it's a tree reference, except that the root of the tree is not
1228
1254
return relpath and osutils.lexists(self.abspath(relpath) + u"/.git")
1230
def extract(self, sub_path, file_id=None, format=None):
1256
def extract(self, sub_path, format=None):
1231
1257
"""Extract a subtree from this tree.
1233
1259
A new branch will be created, relative to the path for this tree.
1289
1315
other_tree = self.revision_tree(revision_id)
1290
1316
except errors.NoSuchRevision:
1291
1317
other_tree = self.branch.repository.revision_tree(
1294
1320
merge.transform_tree(tree, other_tree)
1295
1321
if revision_id == _mod_revision.NULL_REVISION:
1334
1362
if revision_id is not None:
1335
1363
branch.set_last_revision(revision_id)
1336
1364
wt = GitWorkingTree(
1337
a_controldir, a_controldir.open_repository(), branch)
1365
a_controldir, a_controldir.open_repository(), branch)
1338
1366
for hook in MutableTree.hooks['post_build_tree']: