annotate hgext/largefiles/lfutil.py @ 15793:3ef07ecdb0d5

largefiles: correctly handle dirstate status when rebasing When rebasing, we need to trust that the standins are always correct. The rebase operation updates the standins according to the changeset it is rebasing. We need to make the largefiles in the working copy match. If we don't make them match, then they get accidentally reverted, either during the rebase or during the next commit after the rebase. This worked previously only becuase we were relying on the behavior that largefiles with a changed standin, but unchanged contents, never showed up in the list of modified largefiles. Unfortunately, pre-commit hooks can get an incorrect status this way, and it also results in extra execution of code. The solution is to simply trust the standins when we are about to commit a rebased changeset, and politely ask updatelfiles() to pull the new contents down. In this case, updatelfiles() will also mark any files it has pulled down as dirty in the lfdirstate so that pre-commit hooks will get correct status output.
author Na'Tosha Bard <natosha@unity3d.com>
date Sat, 07 Jan 2012 18:43:34 +0100
parents 1facaad963a8
children 0d91211dd12f
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
15168
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
1 # Copyright 2009-2010 Gregory P. Ward
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
3 # Copyright 2010-2011 Fog Creek Software
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
4 # Copyright 2010-2011 Unity Technologies
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
5 #
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
6 # This software may be used and distributed according to the terms of the
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
7 # GNU General Public License version 2 or any later version.
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
8
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
9 '''largefiles utility code: must not import other modules in this package.'''
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
10
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
11 import os
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
12 import errno
15320
681267a5f491 largefiles: use XDG and OS X-specific cache locations by default (issue3067)
Benjamin Pollack <benjamin@bitquabit.com>
parents: 15319
diff changeset
13 import platform
15168
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
14 import shutil
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
15 import stat
15391
a5a6a9b7f3b9 largefiles: replace tempfile.NamedTemporaryFile with tempfile.mkstemp
Hao Lian <hao@fogcreek.com>
parents: 15371
diff changeset
16 import tempfile
15168
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
17
15226
2223ea21c98f largefiles: cleanup import, now that we can assume > 1.9 for bundled extension
Na'Tosha Bard <natosha@unity3d.com>
parents: 15224
diff changeset
18 from mercurial import dirstate, httpconnection, match as match_, util, scmutil
15168
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
19 from mercurial.i18n import _
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
20
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
21 shortname = '.hglf'
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
22 longname = 'largefiles'
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
23
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
24
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
25 # -- Portability wrappers ----------------------------------------------
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
26
15224
7c604d8c7e83 largefiles: remove pre-1.9 code from extension first bundled with 1.9
Na'Tosha Bard <natosha@unity3d.com>
parents: 15206
diff changeset
27 def dirstate_walk(dirstate, matcher, unknown=False, ignored=False):
7c604d8c7e83 largefiles: remove pre-1.9 code from extension first bundled with 1.9
Na'Tosha Bard <natosha@unity3d.com>
parents: 15206
diff changeset
28 return dirstate.walk(matcher, [], unknown, ignored)
15168
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
29
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
30 def repo_add(repo, list):
15224
7c604d8c7e83 largefiles: remove pre-1.9 code from extension first bundled with 1.9
Na'Tosha Bard <natosha@unity3d.com>
parents: 15206
diff changeset
31 add = repo[None].add
15168
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
32 return add(list)
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
33
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
34 def repo_remove(repo, list, unlink=False):
15224
7c604d8c7e83 largefiles: remove pre-1.9 code from extension first bundled with 1.9
Na'Tosha Bard <natosha@unity3d.com>
parents: 15206
diff changeset
35 def remove(list, unlink):
7c604d8c7e83 largefiles: remove pre-1.9 code from extension first bundled with 1.9
Na'Tosha Bard <natosha@unity3d.com>
parents: 15206
diff changeset
36 wlock = repo.wlock()
15168
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
37 try:
15224
7c604d8c7e83 largefiles: remove pre-1.9 code from extension first bundled with 1.9
Na'Tosha Bard <natosha@unity3d.com>
parents: 15206
diff changeset
38 if unlink:
7c604d8c7e83 largefiles: remove pre-1.9 code from extension first bundled with 1.9
Na'Tosha Bard <natosha@unity3d.com>
parents: 15206
diff changeset
39 for f in list:
7c604d8c7e83 largefiles: remove pre-1.9 code from extension first bundled with 1.9
Na'Tosha Bard <natosha@unity3d.com>
parents: 15206
diff changeset
40 try:
7c604d8c7e83 largefiles: remove pre-1.9 code from extension first bundled with 1.9
Na'Tosha Bard <natosha@unity3d.com>
parents: 15206
diff changeset
41 util.unlinkpath(repo.wjoin(f))
7c604d8c7e83 largefiles: remove pre-1.9 code from extension first bundled with 1.9
Na'Tosha Bard <natosha@unity3d.com>
parents: 15206
diff changeset
42 except OSError, inst:
7c604d8c7e83 largefiles: remove pre-1.9 code from extension first bundled with 1.9
Na'Tosha Bard <natosha@unity3d.com>
parents: 15206
diff changeset
43 if inst.errno != errno.ENOENT:
7c604d8c7e83 largefiles: remove pre-1.9 code from extension first bundled with 1.9
Na'Tosha Bard <natosha@unity3d.com>
parents: 15206
diff changeset
44 raise
7c604d8c7e83 largefiles: remove pre-1.9 code from extension first bundled with 1.9
Na'Tosha Bard <natosha@unity3d.com>
parents: 15206
diff changeset
45 repo[None].forget(list)
7c604d8c7e83 largefiles: remove pre-1.9 code from extension first bundled with 1.9
Na'Tosha Bard <natosha@unity3d.com>
parents: 15206
diff changeset
46 finally:
7c604d8c7e83 largefiles: remove pre-1.9 code from extension first bundled with 1.9
Na'Tosha Bard <natosha@unity3d.com>
parents: 15206
diff changeset
47 wlock.release()
15168
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
48 return remove(list, unlink=unlink)
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
49
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
50 def repo_forget(repo, list):
15224
7c604d8c7e83 largefiles: remove pre-1.9 code from extension first bundled with 1.9
Na'Tosha Bard <natosha@unity3d.com>
parents: 15206
diff changeset
51 forget = repo[None].forget
15168
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
52 return forget(list)
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
53
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
54 def findoutgoing(repo, remote, force):
15224
7c604d8c7e83 largefiles: remove pre-1.9 code from extension first bundled with 1.9
Na'Tosha Bard <natosha@unity3d.com>
parents: 15206
diff changeset
55 from mercurial import discovery
7c604d8c7e83 largefiles: remove pre-1.9 code from extension first bundled with 1.9
Na'Tosha Bard <natosha@unity3d.com>
parents: 15206
diff changeset
56 common, _anyinc, _heads = discovery.findcommonincoming(repo,
7c604d8c7e83 largefiles: remove pre-1.9 code from extension first bundled with 1.9
Na'Tosha Bard <natosha@unity3d.com>
parents: 15206
diff changeset
57 remote, force=force)
7c604d8c7e83 largefiles: remove pre-1.9 code from extension first bundled with 1.9
Na'Tosha Bard <natosha@unity3d.com>
parents: 15206
diff changeset
58 return repo.changelog.findmissing(common)
15168
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
59
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
60 # -- Private worker functions ------------------------------------------
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
61
15227
a7686abf73a6 largefiles: factor out lfutil.getminsize()
Greg Ward <greg@gerg.ca>
parents: 15226
diff changeset
62 def getminsize(ui, assumelfiles, opt, default=10):
a7686abf73a6 largefiles: factor out lfutil.getminsize()
Greg Ward <greg@gerg.ca>
parents: 15226
diff changeset
63 lfsize = opt
a7686abf73a6 largefiles: factor out lfutil.getminsize()
Greg Ward <greg@gerg.ca>
parents: 15226
diff changeset
64 if not lfsize and assumelfiles:
15304
9aa9d4bb3d88 largefiles: rename config setting 'size' to 'minsize'
Greg Ward <greg@gerg.ca>
parents: 15255
diff changeset
65 lfsize = ui.config(longname, 'minsize', default=default)
15227
a7686abf73a6 largefiles: factor out lfutil.getminsize()
Greg Ward <greg@gerg.ca>
parents: 15226
diff changeset
66 if lfsize:
a7686abf73a6 largefiles: factor out lfutil.getminsize()
Greg Ward <greg@gerg.ca>
parents: 15226
diff changeset
67 try:
15228
ee625de3541e largefiles: allow minimum size to be a float
Greg Ward <greg@gerg.ca>
parents: 15227
diff changeset
68 lfsize = float(lfsize)
15227
a7686abf73a6 largefiles: factor out lfutil.getminsize()
Greg Ward <greg@gerg.ca>
parents: 15226
diff changeset
69 except ValueError:
15228
ee625de3541e largefiles: allow minimum size to be a float
Greg Ward <greg@gerg.ca>
parents: 15227
diff changeset
70 raise util.Abort(_('largefiles: size must be number (not %s)\n')
15227
a7686abf73a6 largefiles: factor out lfutil.getminsize()
Greg Ward <greg@gerg.ca>
parents: 15226
diff changeset
71 % lfsize)
a7686abf73a6 largefiles: factor out lfutil.getminsize()
Greg Ward <greg@gerg.ca>
parents: 15226
diff changeset
72 if lfsize is None:
a7686abf73a6 largefiles: factor out lfutil.getminsize()
Greg Ward <greg@gerg.ca>
parents: 15226
diff changeset
73 raise util.Abort(_('minimum size for largefiles must be specified'))
a7686abf73a6 largefiles: factor out lfutil.getminsize()
Greg Ward <greg@gerg.ca>
parents: 15226
diff changeset
74 return lfsize
a7686abf73a6 largefiles: factor out lfutil.getminsize()
Greg Ward <greg@gerg.ca>
parents: 15226
diff changeset
75
15168
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
76 def link(src, dest):
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
77 try:
15206
f85c76b16f27 largefiles: fix commit of specified file on non-windows
Na'Tosha Bard <natosha@unity3d.com>
parents: 15188
diff changeset
78 util.oslink(src, dest)
15168
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
79 except OSError:
15572
926bc23d0b6a largefiles: copy files into .hg/largefiles atomically
Martin Geisler <mg@aragost.com>
parents: 15571
diff changeset
80 # if hardlinks fail, fallback on atomic copy
926bc23d0b6a largefiles: copy files into .hg/largefiles atomically
Martin Geisler <mg@aragost.com>
parents: 15571
diff changeset
81 dst = util.atomictempfile(dest)
15699
84e55467093c largefiles: copy files in binary mode (issue3164)
Matt Mackall <mpm@selenic.com>
parents: 15658
diff changeset
82 for chunk in util.filechunkiter(open(src, 'rb')):
15572
926bc23d0b6a largefiles: copy files into .hg/largefiles atomically
Martin Geisler <mg@aragost.com>
parents: 15571
diff changeset
83 dst.write(chunk)
926bc23d0b6a largefiles: copy files into .hg/largefiles atomically
Martin Geisler <mg@aragost.com>
parents: 15571
diff changeset
84 dst.close()
15168
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
85 os.chmod(dest, os.stat(src).st_mode)
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
86
15316
c65f5b6e26d4 largefiles: rename functions and methods to match desired behavior
Benjamin Pollack <benjamin@bitquabit.com>
parents: 15304
diff changeset
87 def usercachepath(ui, hash):
15350
8b8dd13295db largefiles: use ui.configpath() where appropriate
Greg Ward <greg@gerg.ca>
parents: 15349
diff changeset
88 path = ui.configpath(longname, 'usercache', None)
15168
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
89 if path:
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
90 path = os.path.join(path, hash)
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
91 else:
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
92 if os.name == 'nt':
15255
7ab05d752405 largefiles: cosmetics, whitespace, code style
Greg Ward <greg@gerg.ca>
parents: 15253
diff changeset
93 appdata = os.getenv('LOCALAPPDATA', os.getenv('APPDATA'))
15658
971c55ce03b8 largefiles: don't require a user cache (issue3088) (issue3155)
Kevin Gessner <kevin@fogcreek.com>
parents: 15572
diff changeset
94 if appdata:
971c55ce03b8 largefiles: don't require a user cache (issue3088) (issue3155)
Kevin Gessner <kevin@fogcreek.com>
parents: 15572
diff changeset
95 path = os.path.join(appdata, longname, hash)
15320
681267a5f491 largefiles: use XDG and OS X-specific cache locations by default (issue3067)
Benjamin Pollack <benjamin@bitquabit.com>
parents: 15319
diff changeset
96 elif platform.system() == 'Darwin':
15658
971c55ce03b8 largefiles: don't require a user cache (issue3088) (issue3155)
Kevin Gessner <kevin@fogcreek.com>
parents: 15572
diff changeset
97 home = os.getenv('HOME')
971c55ce03b8 largefiles: don't require a user cache (issue3088) (issue3155)
Kevin Gessner <kevin@fogcreek.com>
parents: 15572
diff changeset
98 if home:
971c55ce03b8 largefiles: don't require a user cache (issue3088) (issue3155)
Kevin Gessner <kevin@fogcreek.com>
parents: 15572
diff changeset
99 path = os.path.join(home, 'Library', 'Caches',
971c55ce03b8 largefiles: don't require a user cache (issue3088) (issue3155)
Kevin Gessner <kevin@fogcreek.com>
parents: 15572
diff changeset
100 longname, hash)
15168
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
101 elif os.name == 'posix':
15320
681267a5f491 largefiles: use XDG and OS X-specific cache locations by default (issue3067)
Benjamin Pollack <benjamin@bitquabit.com>
parents: 15319
diff changeset
102 path = os.getenv('XDG_CACHE_HOME')
681267a5f491 largefiles: use XDG and OS X-specific cache locations by default (issue3067)
Benjamin Pollack <benjamin@bitquabit.com>
parents: 15319
diff changeset
103 if path:
681267a5f491 largefiles: use XDG and OS X-specific cache locations by default (issue3067)
Benjamin Pollack <benjamin@bitquabit.com>
parents: 15319
diff changeset
104 path = os.path.join(path, longname, hash)
681267a5f491 largefiles: use XDG and OS X-specific cache locations by default (issue3067)
Benjamin Pollack <benjamin@bitquabit.com>
parents: 15319
diff changeset
105 else:
15658
971c55ce03b8 largefiles: don't require a user cache (issue3088) (issue3155)
Kevin Gessner <kevin@fogcreek.com>
parents: 15572
diff changeset
106 home = os.getenv('HOME')
971c55ce03b8 largefiles: don't require a user cache (issue3088) (issue3155)
Kevin Gessner <kevin@fogcreek.com>
parents: 15572
diff changeset
107 if home:
971c55ce03b8 largefiles: don't require a user cache (issue3088) (issue3155)
Kevin Gessner <kevin@fogcreek.com>
parents: 15572
diff changeset
108 path = os.path.join(home, '.cache', longname, hash)
15168
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
109 else:
15253
67d010779907 largefiles: improve error reporting
Greg Ward <greg@gerg.ca>
parents: 15252
diff changeset
110 raise util.Abort(_('unknown operating system: %s\n') % os.name)
15168
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
111 return path
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
112
15316
c65f5b6e26d4 largefiles: rename functions and methods to match desired behavior
Benjamin Pollack <benjamin@bitquabit.com>
parents: 15304
diff changeset
113 def inusercache(ui, hash):
15658
971c55ce03b8 largefiles: don't require a user cache (issue3088) (issue3155)
Kevin Gessner <kevin@fogcreek.com>
parents: 15572
diff changeset
114 path = usercachepath(ui, hash)
971c55ce03b8 largefiles: don't require a user cache (issue3088) (issue3155)
Kevin Gessner <kevin@fogcreek.com>
parents: 15572
diff changeset
115 return path and os.path.exists(path)
15168
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
116
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
117 def findfile(repo, hash):
15316
c65f5b6e26d4 largefiles: rename functions and methods to match desired behavior
Benjamin Pollack <benjamin@bitquabit.com>
parents: 15304
diff changeset
118 if instore(repo, hash):
c65f5b6e26d4 largefiles: rename functions and methods to match desired behavior
Benjamin Pollack <benjamin@bitquabit.com>
parents: 15304
diff changeset
119 repo.ui.note(_('Found %s in store\n') % hash)
15317
41f371150ccb largefiles: make the store primary, and the user cache secondary
Benjamin Pollack <benjamin@bitquabit.com>
parents: 15316
diff changeset
120 elif inusercache(repo.ui, hash):
15168
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
121 repo.ui.note(_('Found %s in system cache\n') % hash)
15408
db8b0ee74025 largefiles: ensure destination directory exists before findfile links to there
Hao Lian <hao@fogcreek.com>
parents: 15392
diff changeset
122 path = storepath(repo, hash)
db8b0ee74025 largefiles: ensure destination directory exists before findfile links to there
Hao Lian <hao@fogcreek.com>
parents: 15392
diff changeset
123 util.makedirs(os.path.dirname(path))
db8b0ee74025 largefiles: ensure destination directory exists before findfile links to there
Hao Lian <hao@fogcreek.com>
parents: 15392
diff changeset
124 link(usercachepath(repo.ui, hash), path)
15317
41f371150ccb largefiles: make the store primary, and the user cache secondary
Benjamin Pollack <benjamin@bitquabit.com>
parents: 15316
diff changeset
125 else:
41f371150ccb largefiles: make the store primary, and the user cache secondary
Benjamin Pollack <benjamin@bitquabit.com>
parents: 15316
diff changeset
126 return None
41f371150ccb largefiles: make the store primary, and the user cache secondary
Benjamin Pollack <benjamin@bitquabit.com>
parents: 15316
diff changeset
127 return storepath(repo, hash)
15168
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
128
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
129 class largefiles_dirstate(dirstate.dirstate):
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
130 def __getitem__(self, key):
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
131 return super(largefiles_dirstate, self).__getitem__(unixpath(key))
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
132 def normal(self, f):
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
133 return super(largefiles_dirstate, self).normal(unixpath(f))
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
134 def remove(self, f):
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
135 return super(largefiles_dirstate, self).remove(unixpath(f))
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
136 def add(self, f):
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
137 return super(largefiles_dirstate, self).add(unixpath(f))
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
138 def drop(self, f):
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
139 return super(largefiles_dirstate, self).drop(unixpath(f))
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
140 def forget(self, f):
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
141 return super(largefiles_dirstate, self).forget(unixpath(f))
15793
3ef07ecdb0d5 largefiles: correctly handle dirstate status when rebasing
Na'Tosha Bard <natosha@unity3d.com>
parents: 15700
diff changeset
142 def normallookup(self, f):
3ef07ecdb0d5 largefiles: correctly handle dirstate status when rebasing
Na'Tosha Bard <natosha@unity3d.com>
parents: 15700
diff changeset
143 return super(largefiles_dirstate, self).normallookup(unixpath(f))
15168
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
144
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
145 def openlfdirstate(ui, repo):
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
146 '''
15252
6e809bb4f969 largefiles: improve comments, internal docstrings
Greg Ward <greg@gerg.ca>
parents: 15228
diff changeset
147 Return a dirstate object that tracks largefiles: i.e. its root is
6e809bb4f969 largefiles: improve comments, internal docstrings
Greg Ward <greg@gerg.ca>
parents: 15228
diff changeset
148 the repo root, but it is saved in .hg/largefiles/dirstate.
15168
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
149 '''
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
150 admin = repo.join(longname)
15224
7c604d8c7e83 largefiles: remove pre-1.9 code from extension first bundled with 1.9
Na'Tosha Bard <natosha@unity3d.com>
parents: 15206
diff changeset
151 opener = scmutil.opener(admin)
15349
63455eb771af largefiles: drop more unnecessary compatibility checks
Greg Ward <greg@gerg.ca>
parents: 15347
diff changeset
152 lfdirstate = largefiles_dirstate(opener, ui, repo.root,
63455eb771af largefiles: drop more unnecessary compatibility checks
Greg Ward <greg@gerg.ca>
parents: 15347
diff changeset
153 repo.dirstate._validate)
15168
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
154
15252
6e809bb4f969 largefiles: improve comments, internal docstrings
Greg Ward <greg@gerg.ca>
parents: 15228
diff changeset
155 # If the largefiles dirstate does not exist, populate and create
6e809bb4f969 largefiles: improve comments, internal docstrings
Greg Ward <greg@gerg.ca>
parents: 15228
diff changeset
156 # it. This ensures that we create it on the first meaningful
6e809bb4f969 largefiles: improve comments, internal docstrings
Greg Ward <greg@gerg.ca>
parents: 15228
diff changeset
157 # largefiles operation in a new clone. It also gives us an easy
6e809bb4f969 largefiles: improve comments, internal docstrings
Greg Ward <greg@gerg.ca>
parents: 15228
diff changeset
158 # way to forcibly rebuild largefiles state:
15168
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
159 # rm .hg/largefiles/dirstate && hg status
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
160 # Or even, if things are really messed up:
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
161 # rm -rf .hg/largefiles && hg status
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
162 if not os.path.exists(os.path.join(admin, 'dirstate')):
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
163 util.makedirs(admin)
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
164 matcher = getstandinmatcher(repo)
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
165 for standin in dirstate_walk(repo.dirstate, matcher):
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
166 lfile = splitstandin(standin)
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
167 hash = readstandin(repo, lfile)
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
168 lfdirstate.normallookup(lfile)
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
169 try:
15553
e89385e4ef8d largefiles: file storage should be relative to repo, not relative to cwd
Mads Kiilerich <mads@kiilerich.com>
parents: 15548
diff changeset
170 if hash == hashfile(repo.wjoin(lfile)):
15168
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
171 lfdirstate.normal(lfile)
15548
f76584098c88 largefiles: fix 'hg clone . ../foo' OSError abort
Martin Geisler <mg@lazybytes.net>
parents: 15408
diff changeset
172 except OSError, err:
15168
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
173 if err.errno != errno.ENOENT:
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
174 raise
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
175
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
176 lfdirstate.write()
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
177
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
178 return lfdirstate
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
179
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
180 def lfdirstate_status(lfdirstate, repo, rev):
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
181 wlock = repo.wlock()
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
182 try:
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
183 match = match_.always(repo.root, repo.getcwd())
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
184 s = lfdirstate.status(match, [], False, False, False)
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
185 unsure, modified, added, removed, missing, unknown, ignored, clean = s
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
186 for lfile in unsure:
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
187 if repo[rev][standin(lfile)].data().strip() != \
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
188 hashfile(repo.wjoin(lfile)):
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
189 modified.append(lfile)
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
190 else:
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
191 clean.append(lfile)
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
192 lfdirstate.normal(lfile)
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
193 lfdirstate.write()
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
194 finally:
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
195 wlock.release()
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
196 return (modified, added, removed, missing, unknown, ignored, clean)
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
197
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
198 def listlfiles(repo, rev=None, matcher=None):
15252
6e809bb4f969 largefiles: improve comments, internal docstrings
Greg Ward <greg@gerg.ca>
parents: 15228
diff changeset
199 '''return a list of largefiles in the working copy or the
6e809bb4f969 largefiles: improve comments, internal docstrings
Greg Ward <greg@gerg.ca>
parents: 15228
diff changeset
200 specified changeset'''
15168
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
201
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
202 if matcher is None:
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
203 matcher = getstandinmatcher(repo)
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
204
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
205 # ignore unknown files in working directory
15255
7ab05d752405 largefiles: cosmetics, whitespace, code style
Greg Ward <greg@gerg.ca>
parents: 15253
diff changeset
206 return [splitstandin(f)
7ab05d752405 largefiles: cosmetics, whitespace, code style
Greg Ward <greg@gerg.ca>
parents: 15253
diff changeset
207 for f in repo[rev].walk(matcher)
15168
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
208 if rev is not None or repo.dirstate[f] != '?']
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
209
15316
c65f5b6e26d4 largefiles: rename functions and methods to match desired behavior
Benjamin Pollack <benjamin@bitquabit.com>
parents: 15304
diff changeset
210 def instore(repo, hash):
c65f5b6e26d4 largefiles: rename functions and methods to match desired behavior
Benjamin Pollack <benjamin@bitquabit.com>
parents: 15304
diff changeset
211 return os.path.exists(storepath(repo, hash))
15168
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
212
15316
c65f5b6e26d4 largefiles: rename functions and methods to match desired behavior
Benjamin Pollack <benjamin@bitquabit.com>
parents: 15304
diff changeset
213 def storepath(repo, hash):
15168
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
214 return repo.join(os.path.join(longname, hash))
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
215
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
216 def copyfromcache(repo, hash, filename):
15252
6e809bb4f969 largefiles: improve comments, internal docstrings
Greg Ward <greg@gerg.ca>
parents: 15228
diff changeset
217 '''Copy the specified largefile from the repo or system cache to
6e809bb4f969 largefiles: improve comments, internal docstrings
Greg Ward <greg@gerg.ca>
parents: 15228
diff changeset
218 filename in the repository. Return true on success or false if the
6e809bb4f969 largefiles: improve comments, internal docstrings
Greg Ward <greg@gerg.ca>
parents: 15228
diff changeset
219 file was not found in either cache (which should not happened:
6e809bb4f969 largefiles: improve comments, internal docstrings
Greg Ward <greg@gerg.ca>
parents: 15228
diff changeset
220 this is meant to be called only after ensuring that the needed
6e809bb4f969 largefiles: improve comments, internal docstrings
Greg Ward <greg@gerg.ca>
parents: 15228
diff changeset
221 largefile exists in the cache).'''
15168
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
222 path = findfile(repo, hash)
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
223 if path is None:
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
224 return False
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
225 util.makedirs(os.path.dirname(repo.wjoin(filename)))
15570
0f208626d503 largefiles: add comment about non-atomic working directory
Martin Geisler <mg@aragost.com>
parents: 15553
diff changeset
226 # The write may fail before the file is fully written, but we
0f208626d503 largefiles: add comment about non-atomic working directory
Martin Geisler <mg@aragost.com>
parents: 15553
diff changeset
227 # don't use atomic writes in the working copy.
15168
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
228 shutil.copy(path, repo.wjoin(filename))
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
229 return True
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
230
15316
c65f5b6e26d4 largefiles: rename functions and methods to match desired behavior
Benjamin Pollack <benjamin@bitquabit.com>
parents: 15304
diff changeset
231 def copytostore(repo, rev, file, uploaded=False):
15168
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
232 hash = readstandin(repo, file)
15316
c65f5b6e26d4 largefiles: rename functions and methods to match desired behavior
Benjamin Pollack <benjamin@bitquabit.com>
parents: 15304
diff changeset
233 if instore(repo, hash):
15168
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
234 return
15316
c65f5b6e26d4 largefiles: rename functions and methods to match desired behavior
Benjamin Pollack <benjamin@bitquabit.com>
parents: 15304
diff changeset
235 copytostoreabsolute(repo, repo.wjoin(file), hash)
15168
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
236
15316
c65f5b6e26d4 largefiles: rename functions and methods to match desired behavior
Benjamin Pollack <benjamin@bitquabit.com>
parents: 15304
diff changeset
237 def copytostoreabsolute(repo, file, hash):
15371
f26ed4ea46d8 largefiles: remove lfutil.createdir, replace calls with util.makedirs
Hao Lian <hao@fogcreek.com>
parents: 15350
diff changeset
238 util.makedirs(os.path.dirname(storepath(repo, hash)))
15316
c65f5b6e26d4 largefiles: rename functions and methods to match desired behavior
Benjamin Pollack <benjamin@bitquabit.com>
parents: 15304
diff changeset
239 if inusercache(repo.ui, hash):
c65f5b6e26d4 largefiles: rename functions and methods to match desired behavior
Benjamin Pollack <benjamin@bitquabit.com>
parents: 15304
diff changeset
240 link(usercachepath(repo.ui, hash), storepath(repo, hash))
15168
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
241 else:
15571
809788118aa2 largefiles: write .hg/largefiles/ files atomically
Martin Geisler <mg@aragost.com>
parents: 15570
diff changeset
242 dst = util.atomictempfile(storepath(repo, hash))
15699
84e55467093c largefiles: copy files in binary mode (issue3164)
Matt Mackall <mpm@selenic.com>
parents: 15658
diff changeset
243 for chunk in util.filechunkiter(open(file, 'rb')):
15571
809788118aa2 largefiles: write .hg/largefiles/ files atomically
Martin Geisler <mg@aragost.com>
parents: 15570
diff changeset
244 dst.write(chunk)
809788118aa2 largefiles: write .hg/largefiles/ files atomically
Martin Geisler <mg@aragost.com>
parents: 15570
diff changeset
245 dst.close()
809788118aa2 largefiles: write .hg/largefiles/ files atomically
Martin Geisler <mg@aragost.com>
parents: 15570
diff changeset
246 util.copymode(file, storepath(repo, hash))
15316
c65f5b6e26d4 largefiles: rename functions and methods to match desired behavior
Benjamin Pollack <benjamin@bitquabit.com>
parents: 15304
diff changeset
247 linktousercache(repo, hash)
15168
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
248
15316
c65f5b6e26d4 largefiles: rename functions and methods to match desired behavior
Benjamin Pollack <benjamin@bitquabit.com>
parents: 15304
diff changeset
249 def linktousercache(repo, hash):
15658
971c55ce03b8 largefiles: don't require a user cache (issue3088) (issue3155)
Kevin Gessner <kevin@fogcreek.com>
parents: 15572
diff changeset
250 path = usercachepath(repo.ui, hash)
971c55ce03b8 largefiles: don't require a user cache (issue3088) (issue3155)
Kevin Gessner <kevin@fogcreek.com>
parents: 15572
diff changeset
251 if path:
971c55ce03b8 largefiles: don't require a user cache (issue3088) (issue3155)
Kevin Gessner <kevin@fogcreek.com>
parents: 15572
diff changeset
252 util.makedirs(os.path.dirname(path))
971c55ce03b8 largefiles: don't require a user cache (issue3088) (issue3155)
Kevin Gessner <kevin@fogcreek.com>
parents: 15572
diff changeset
253 link(storepath(repo, hash), path)
15168
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
254
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
255 def getstandinmatcher(repo, pats=[], opts={}):
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
256 '''Return a match object that applies pats to the standin directory'''
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
257 standindir = repo.pathto(shortname)
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
258 if pats:
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
259 # patterns supplied: search standin directory relative to current dir
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
260 cwd = repo.getcwd()
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
261 if os.path.isabs(cwd):
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
262 # cwd is an absolute path for hg -R <reponame>
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
263 # work relative to the repository root in this case
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
264 cwd = ''
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
265 pats = [os.path.join(standindir, cwd, pat) for pat in pats]
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
266 elif os.path.isdir(standindir):
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
267 # no patterns: relative to repo root
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
268 pats = [standindir]
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
269 else:
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
270 # no patterns and no standin dir: return matcher that matches nothing
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
271 match = match_.match(repo.root, None, [], exact=True)
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
272 match.matchfn = lambda f: False
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
273 return match
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
274 return getmatcher(repo, pats, opts, showbad=False)
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
275
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
276 def getmatcher(repo, pats=[], opts={}, showbad=True):
15252
6e809bb4f969 largefiles: improve comments, internal docstrings
Greg Ward <greg@gerg.ca>
parents: 15228
diff changeset
277 '''Wrapper around scmutil.match() that adds showbad: if false,
6e809bb4f969 largefiles: improve comments, internal docstrings
Greg Ward <greg@gerg.ca>
parents: 15228
diff changeset
278 neuter the match object's bad() method so it does not print any
6e809bb4f969 largefiles: improve comments, internal docstrings
Greg Ward <greg@gerg.ca>
parents: 15228
diff changeset
279 warnings about missing files or directories.'''
15224
7c604d8c7e83 largefiles: remove pre-1.9 code from extension first bundled with 1.9
Na'Tosha Bard <natosha@unity3d.com>
parents: 15206
diff changeset
280 match = scmutil.match(repo[None], pats, opts)
15168
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
281
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
282 if not showbad:
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
283 match.bad = lambda f, msg: None
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
284 return match
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
285
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
286 def composestandinmatcher(repo, rmatcher):
15252
6e809bb4f969 largefiles: improve comments, internal docstrings
Greg Ward <greg@gerg.ca>
parents: 15228
diff changeset
287 '''Return a matcher that accepts standins corresponding to the
6e809bb4f969 largefiles: improve comments, internal docstrings
Greg Ward <greg@gerg.ca>
parents: 15228
diff changeset
288 files accepted by rmatcher. Pass the list of files in the matcher
6e809bb4f969 largefiles: improve comments, internal docstrings
Greg Ward <greg@gerg.ca>
parents: 15228
diff changeset
289 as the paths specified by the user.'''
15168
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
290 smatcher = getstandinmatcher(repo, rmatcher.files())
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
291 isstandin = smatcher.matchfn
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
292 def composed_matchfn(f):
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
293 return isstandin(f) and rmatcher.matchfn(splitstandin(f))
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
294 smatcher.matchfn = composed_matchfn
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
295
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
296 return smatcher
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
297
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
298 def standin(filename):
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
299 '''Return the repo-relative path to the standin for the specified big
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
300 file.'''
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
301 # Notes:
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
302 # 1) Most callers want an absolute path, but _create_standin() needs
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
303 # it repo-relative so lfadd() can pass it to repo_add(). So leave
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
304 # it up to the caller to use repo.wjoin() to get an absolute path.
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
305 # 2) Join with '/' because that's what dirstate always uses, even on
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
306 # Windows. Change existing separator to '/' first in case we are
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
307 # passed filenames from an external source (like the command line).
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
308 return shortname + '/' + filename.replace(os.sep, '/')
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
309
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
310 def isstandin(filename):
15252
6e809bb4f969 largefiles: improve comments, internal docstrings
Greg Ward <greg@gerg.ca>
parents: 15228
diff changeset
311 '''Return true if filename is a big file standin. filename must be
6e809bb4f969 largefiles: improve comments, internal docstrings
Greg Ward <greg@gerg.ca>
parents: 15228
diff changeset
312 in Mercurial's internal form (slash-separated).'''
15168
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
313 return filename.startswith(shortname + '/')
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
314
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
315 def splitstandin(filename):
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
316 # Split on / because that's what dirstate always uses, even on Windows.
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
317 # Change local separator to / first just in case we are passed filenames
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
318 # from an external source (like the command line).
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
319 bits = filename.replace(os.sep, '/').split('/', 1)
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
320 if len(bits) == 2 and bits[0] == shortname:
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
321 return bits[1]
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
322 else:
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
323 return None
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
324
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
325 def updatestandin(repo, standin):
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
326 file = repo.wjoin(splitstandin(standin))
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
327 if os.path.exists(file):
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
328 hash = hashfile(file)
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
329 executable = getexecutable(file)
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
330 writestandin(repo, standin, hash, executable)
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
331
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
332 def readstandin(repo, filename, node=None):
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
333 '''read hex hash from standin for filename at given node, or working
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
334 directory if no node is given'''
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
335 return repo[node][standin(filename)].data().strip()
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
336
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
337 def writestandin(repo, standin, hash, executable):
15252
6e809bb4f969 largefiles: improve comments, internal docstrings
Greg Ward <greg@gerg.ca>
parents: 15228
diff changeset
338 '''write hash to <repo.root>/<standin>'''
15168
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
339 writehash(hash, repo.wjoin(standin), executable)
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
340
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
341 def copyandhash(instream, outfile):
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
342 '''Read bytes from instream (iterable) and write them to outfile,
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
343 computing the SHA-1 hash of the data along the way. Close outfile
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
344 when done and return the binary hash.'''
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
345 hasher = util.sha1('')
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
346 for data in instream:
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
347 hasher.update(data)
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
348 outfile.write(data)
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
349
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
350 # Blecch: closing a file that somebody else opened is rude and
15252
6e809bb4f969 largefiles: improve comments, internal docstrings
Greg Ward <greg@gerg.ca>
parents: 15228
diff changeset
351 # wrong. But it's so darn convenient and practical! After all,
15168
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
352 # outfile was opened just to copy and hash.
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
353 outfile.close()
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
354
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
355 return hasher.digest()
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
356
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
357 def hashrepofile(repo, file):
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
358 return hashfile(repo.wjoin(file))
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
359
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
360 def hashfile(file):
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
361 if not os.path.exists(file):
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
362 return ''
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
363 hasher = util.sha1('')
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
364 fd = open(file, 'rb')
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
365 for data in blockstream(fd):
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
366 hasher.update(data)
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
367 fd.close()
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
368 return hasher.hexdigest()
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
369
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
370 class limitreader(object):
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
371 def __init__(self, f, limit):
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
372 self.f = f
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
373 self.limit = limit
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
374
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
375 def read(self, length):
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
376 if self.limit == 0:
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
377 return ''
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
378 length = length > self.limit and self.limit or length
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
379 self.limit -= length
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
380 return self.f.read(length)
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
381
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
382 def close(self):
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
383 pass
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
384
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
385 def blockstream(infile, blocksize=128 * 1024):
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
386 """Generator that yields blocks of data from infile and closes infile."""
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
387 while True:
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
388 data = infile.read(blocksize)
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
389 if not data:
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
390 break
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
391 yield data
15252
6e809bb4f969 largefiles: improve comments, internal docstrings
Greg Ward <greg@gerg.ca>
parents: 15228
diff changeset
392 # same blecch as copyandhash() above
15168
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
393 infile.close()
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
394
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
395 def readhash(filename):
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
396 rfile = open(filename, 'rb')
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
397 hash = rfile.read(40)
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
398 rfile.close()
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
399 if len(hash) < 40:
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
400 raise util.Abort(_('bad hash in \'%s\' (only %d bytes long)')
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
401 % (filename, len(hash)))
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
402 return hash
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
403
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
404 def writehash(hash, filename, executable):
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
405 util.makedirs(os.path.dirname(filename))
15574
c9328c829cd9 largefiles: simplify lfutil.writehash
Martin Geisler <mg@aragost.com>
parents: 15572
diff changeset
406 util.writefile(filename, hash + '\n')
c9328c829cd9 largefiles: simplify lfutil.writehash
Martin Geisler <mg@aragost.com>
parents: 15572
diff changeset
407 os.chmod(filename, getmode(executable))
15168
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
408
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
409 def getexecutable(filename):
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
410 mode = os.stat(filename).st_mode
15255
7ab05d752405 largefiles: cosmetics, whitespace, code style
Greg Ward <greg@gerg.ca>
parents: 15253
diff changeset
411 return ((mode & stat.S_IXUSR) and
7ab05d752405 largefiles: cosmetics, whitespace, code style
Greg Ward <greg@gerg.ca>
parents: 15253
diff changeset
412 (mode & stat.S_IXGRP) and
7ab05d752405 largefiles: cosmetics, whitespace, code style
Greg Ward <greg@gerg.ca>
parents: 15253
diff changeset
413 (mode & stat.S_IXOTH))
15168
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
414
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
415 def getmode(executable):
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
416 if executable:
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
417 return 0755
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
418 else:
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
419 return 0644
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
420
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
421 def urljoin(first, second, *arg):
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
422 def join(left, right):
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
423 if not left.endswith('/'):
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
424 left += '/'
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
425 if right.startswith('/'):
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
426 right = right[1:]
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
427 return left + right
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
428
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
429 url = join(first, second)
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
430 for a in arg:
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
431 url = join(url, a)
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
432 return url
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
433
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
434 def hexsha1(data):
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
435 """hexsha1 returns the hex-encoded sha1 sum of the data in the file-like
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
436 object data"""
15347
799e56609ef6 largefiles: use util.sha1() instead of hashlib.sha1() everywhere
Thomas Arendsen Hein <thomas@intevation.de>
parents: 15333
diff changeset
437 h = util.sha1()
15168
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
438 for chunk in util.filechunkiter(data):
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
439 h.update(chunk)
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
440 return h.hexdigest()
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
441
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
442 def httpsendfile(ui, filename):
15224
7c604d8c7e83 largefiles: remove pre-1.9 code from extension first bundled with 1.9
Na'Tosha Bard <natosha@unity3d.com>
parents: 15206
diff changeset
443 return httpconnection.httpsendfile(ui, filename, 'rb')
15168
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
444
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
445 def unixpath(path):
15252
6e809bb4f969 largefiles: improve comments, internal docstrings
Greg Ward <greg@gerg.ca>
parents: 15228
diff changeset
446 '''Return a version of path normalized for use with the lfdirstate.'''
15168
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
447 return os.path.normpath(path).replace(os.sep, '/')
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
448
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
449 def islfilesrepo(repo):
15170
c1a4a3220711 largefiles: fix over-long lines
Matt Mackall <mpm@selenic.com>
parents: 15169
diff changeset
450 return ('largefiles' in repo.requirements and
15319
9da7e96cd5c2 largefiles: remove redundant any_ function
Benjamin Pollack <benjamin@bitquabit.com>
parents: 15317
diff changeset
451 util.any(shortname + '/' in f[0] for f in repo.store.datafiles()))
15168
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
452
15391
a5a6a9b7f3b9 largefiles: replace tempfile.NamedTemporaryFile with tempfile.mkstemp
Hao Lian <hao@fogcreek.com>
parents: 15371
diff changeset
453 def mkstemp(repo, prefix):
a5a6a9b7f3b9 largefiles: replace tempfile.NamedTemporaryFile with tempfile.mkstemp
Hao Lian <hao@fogcreek.com>
parents: 15371
diff changeset
454 '''Returns a file descriptor and a filename corresponding to a temporary
a5a6a9b7f3b9 largefiles: replace tempfile.NamedTemporaryFile with tempfile.mkstemp
Hao Lian <hao@fogcreek.com>
parents: 15371
diff changeset
455 file in the repo's largefiles store.'''
a5a6a9b7f3b9 largefiles: replace tempfile.NamedTemporaryFile with tempfile.mkstemp
Hao Lian <hao@fogcreek.com>
parents: 15371
diff changeset
456 path = repo.join(longname)
15392
d7bfbc92a1c0 util: add a doctest for empty sha() calls
Matt Mackall <mpm@selenic.com>
parents: 15391
diff changeset
457 util.makedirs(path)
15391
a5a6a9b7f3b9 largefiles: replace tempfile.NamedTemporaryFile with tempfile.mkstemp
Hao Lian <hao@fogcreek.com>
parents: 15371
diff changeset
458 return tempfile.mkstemp(prefix=prefix, dir=path)
a5a6a9b7f3b9 largefiles: replace tempfile.NamedTemporaryFile with tempfile.mkstemp
Hao Lian <hao@fogcreek.com>
parents: 15371
diff changeset
459
15333
f37b71fec602 largefiles: py2.4 doesn't have BaseException
Matt Mackall <mpm@selenic.com>
parents: 15320
diff changeset
460 class storeprotonotcapable(Exception):
15168
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
461 def __init__(self, storetypes):
cfccd3bee7b3 hgext: add largefiles extension
various
parents:
diff changeset
462 self.storetypes = storetypes