43
def find_branch_root(f=None):
44
"""Find the branch root enclosing f, or pwd.
46
It is not necessary that f exists.
48
Basically we keep looking up until we find the control directory or
52
elif hasattr(os.path, 'realpath'):
53
f = os.path.realpath(f)
55
f = os.path.abspath(f)
61
if os.path.exists(os.path.join(f, bzrlib.BZRDIR)):
63
head, tail = os.path.split(f)
65
# reached the root, whatever that may be
66
bailout('%r is not in a branch' % orig_f)
71
46
######################################################################
88
63
:todo: mkdir() method.
90
def __init__(self, base, init=False, find_root=True):
65
def __init__(self, base, init=False):
91
66
"""Create new branch object at a particular location.
93
68
:param base: Base directory for the branch.
95
70
:param init: If True, create new control files in a previously
96
71
unversioned directory. If False, the branch must already
99
:param find_root: If true and init is false, find the root of the
100
existing branch containing base.
102
74
In the test suite, creation of new trees is tested using the
103
75
`ScratchBranch` class.
77
self.base = os.path.realpath(base)
106
self.base = os.path.realpath(base)
107
79
self._make_control()
109
self.base = find_branch_root(base)
111
self.base = os.path.realpath(base)
112
81
if not isdir(self.controlfilename('.')):
113
82
bailout("not a bzr branch: %s" % quotefn(base),
114
83
['use "bzr init" to initialize a new working tree',
115
84
'current bzr can only operate from top-of-tree'])
118
87
self.text_store = ImmutableStore(self.controlfilename('text-store'))
119
88
self.revision_store = ImmutableStore(self.controlfilename('revision-store'))
127
96
__repr__ = __str__
130
def abspath(self, name):
131
"""Return absolute filename for something in the branch"""
100
"""Return filename relative to branch top"""
132
101
return os.path.join(self.base, name)
135
def relpath(self, path):
136
"""Return path relative to this branch of something inside it.
138
Raises an error if path is not in this branch."""
139
rp = os.path.realpath(path)
141
if not rp.startswith(self.base):
142
bailout("path %r is not within branch %r" % (rp, self.base))
143
rp = rp[len(self.base):]
144
rp = rp.lstrip(os.sep)
148
104
def controlfilename(self, file_or_path):
149
105
"""Return location relative to branch."""
204
160
will be committed to the next revision.
206
162
## TODO: factor out to atomicfile? is rename safe on windows?
207
## TODO: Maybe some kind of clean/dirty marker on inventory?
208
163
tmpfname = self.controlfilename('inventory.tmp')
209
164
tmpf = file(tmpfname, 'w')
210
165
inv.write_xml(tmpf)
274
229
bailout("cannot add top-level %r" % f)
276
fullpath = os.path.normpath(self.abspath(f))
279
kind = file_kind(fullpath)
281
# maybe something better?
282
bailout('cannot add: not a regular file or directory: %s' % quotefn(f))
284
if kind != 'file' and kind != 'directory':
285
bailout('cannot add: not a regular file or directory: %s' % quotefn(f))
287
file_id = gen_file_id(f)
288
inv.add_path(f, kind=kind, file_id=file_id)
231
fullpath = os.path.normpath(self._rel(f))
235
elif isdir(fullpath):
238
bailout('cannot add: not a regular file or directory: %s' % quotefn(f))
241
parent_name = joinpath(fp[:-1])
242
mutter("lookup parent %r" % parent_name)
243
parent_id = inv.path2id(parent_name)
244
if parent_id == None:
245
bailout("cannot add: parent %r is not versioned"
250
file_id = _gen_file_id(fp[-1])
251
inv.add(InventoryEntry(file_id, fp[-1], kind=kind, parent_id=parent_id))
291
253
show_status('A', kind, quotefn(f))
293
mutter("add file %s file_id:{%s} kind=%r" % (f, file_id, kind))
255
mutter("add file %s file_id:{%s} kind=%r parent_id={%s}"
256
% (f, file_id, kind, parent_id))
295
257
self._write_inventory(inv)
475
entry.text_id = gen_file_id(entry.name)
437
entry.text_id = _gen_file_id(entry.name)
476
438
self.text_store.add(content, entry.text_id)
477
439
mutter(' stored with text_id {%s}' % entry.text_id)
811
def gen_file_id(name):
771
def _gen_file_id(name):
812
772
"""Return new file id.
814
774
This should probably generate proper UUIDs, but for the moment we
815
775
cope with just randomness because running uuidgen every time is
817
idx = name.rfind('/')
819
name = name[idx+1 : ]
821
name = name.lstrip('.')
777
assert '/' not in name
778
while name[0] == '.':
823
780
s = hexlify(rand_bytes(8))
824
781
return '-'.join((name, compact_date(time.time()), s))