##// END OF EJS Templates
merge with stable
merge with stable

File last commit:

r15410:9e99d2bb default
r15426:dc9d0189 merge default
Show More
subrepo.py
1117 lines | 41.9 KiB | text/x-python | PythonLexer
Matt Mackall
subrepo: introduce basic state parsing
r8812 # subrepo.py - sub-repository handling for Mercurial
#
David Soria Parra
subrepo: correct copyright
r10324 # Copyright 2009-2010 Matt Mackall <mpm@selenic.com>
Matt Mackall
subrepo: introduce basic state parsing
r8812 #
# This software may be used and distributed according to the terms of the
Matt Mackall
Update license to GPLv2+
r10263 # GNU General Public License version 2 or any later version.
Matt Mackall
subrepo: introduce basic state parsing
r8812
Brodie Rao
subrepos: use url.url when normalizing repo paths...
r13771 import errno, os, re, xml.dom.minidom, shutil, posixpath
Eric Eisner
subrepo: archive git subrepos
r13027 import stat, subprocess, tarfile
Matt Mackall
subrepo: add update/merge logic
r8814 from i18n import _
Brodie Rao
url: move URL parsing functions into util to improve startup time...
r14076 import config, scmutil, util, node, error, cmdutil, bookmarks
Abderrahim Kitouni
subrepo: use hg.repository instead of creating localrepo directly...
r9092 hg = None
Patrick Mezard
subrepo: handle svn tracked/unknown directory collisions...
r14050 propertycache = util.propertycache
Matt Mackall
commit: recurse into subrepositories
r8813
Augie Fackler
subrepo: add table-based dispatch for subrepo types
r10177 nullstate = ('', '', 'empty')
Matt Mackall
subrepo: introduce basic state parsing
r8812
Martin Geisler
subrepos: support remapping of .hgsub source paths...
r11775 def state(ctx, ui):
Mads Kiilerich
subrepo: docstrings
r11571 """return a state dict, mapping subrepo paths configured in .hgsub
to tuple: (source from .hgsub, revision from .hgsubstate, kind
(key in types dict))
"""
Matt Mackall
subrepo: introduce basic state parsing
r8812 p = config.config()
def read(f, sections=None, remap=None):
if f in ctx:
Patrick Mezard
subrepo: handle missing subrepo spec file as removed...
r13017 try:
data = ctx[f].data()
except IOError, err:
if err.errno != errno.ENOENT:
raise
# handle missing subrepo spec files as removed
ui.warn(_("warning: subrepo spec file %s not found\n") % f)
return
p.parse(f, data, sections, remap, read)
Matt Mackall
subrepo: fix includes support in .hgsub
r10174 else:
raise util.Abort(_("subrepo spec file %s not found") % f)
if '.hgsub' in ctx:
read('.hgsub')
Matt Mackall
subrepo: introduce basic state parsing
r8812
Martin Geisler
subrepos: support remapping of .hgsub source paths...
r11775 for path, src in ui.configitems('subpaths'):
p.set('subpaths', path, src, ui.configsource('subpaths', path))
Matt Mackall
subrepo: introduce basic state parsing
r8812 rev = {}
if '.hgsubstate' in ctx:
try:
for l in ctx['.hgsubstate'].data().splitlines():
Matt Mackall
subrepo: more robust split for .hgsubstate parsing
r9752 revision, path = l.split(" ", 1)
Matt Mackall
subrepo: introduce basic state parsing
r8812 rev[path] = revision
except IOError, err:
if err.errno != errno.ENOENT:
raise
Martin Geisler
subrepo: refactor state function
r15149 def remap(src):
Martin Geisler
subrepos: support remapping of .hgsub source paths...
r11775 for pattern, repl in p.items('subpaths'):
Martin Geisler
subrepos: handle backslashes in subpaths
r11961 # Turn r'C:\foo\bar' into r'C:\\foo\\bar' since re.sub
# does a string decode.
repl = repl.encode('string-escape')
# However, we still want to allow back references to go
# through unharmed, so we turn r'\\1' into r'\1'. Again,
# extra escapes are needed because re.sub string decodes.
repl = re.sub(r'\\\\([0-9]+)', r'\\\1', repl)
Martin Geisler
subrepos: support remapping of .hgsub source paths...
r11775 try:
src = re.sub(pattern, repl, src, 1)
except re.error, e:
raise util.Abort(_("bad subrepository pattern in %s: %s")
% (p.source('subpaths', pattern), e))
Martin Geisler
subrepo: refactor state function
r15149 return src
Martin Geisler
subrepos: support remapping of .hgsub source paths...
r11775
Martin Geisler
subrepo: refactor state function
r15149 state = {}
for path, src in p[''].items():
kind = 'hg'
if src.startswith('['):
if ']' not in src:
raise util.Abort(_('missing ] in subrepo source'))
kind, src = src.split(']', 1)
kind = kind[1:]
Martin Geisler
subrepo: try remapping subpaths using the "final" path...
r15150 src = src.lstrip() # strip any extra whitespace after ']'
if not util.url(src).isabs():
parent = _abssource(ctx._repo, abort=False)
if parent:
parent = util.url(parent)
parent.path = posixpath.join(parent.path or '', src)
parent.path = posixpath.normpath(parent.path)
joined = str(parent)
# Remap the full joined path and use it if it changes,
# else remap the original source.
remapped = remap(joined)
if remapped == joined:
src = remap(src)
else:
src = remapped
Martin Geisler
subrepo: refactor state function
r15149 src = remap(src)
David Soria Parra
subrepo: make sure that the source path is stripped...
r10457 state[path] = (src.strip(), rev.get(path, ''), kind)
Matt Mackall
subrepo: introduce basic state parsing
r8812
return state
Matt Mackall
commit: recurse into subrepositories
r8813
def writestate(repo, state):
Mads Kiilerich
subrepo: docstrings
r11571 """rewrite .hgsubstate in (outer) repo with these subrepo states"""
Martin Geisler
subrepo: refactor writestate for clarity
r14443 lines = ['%s %s\n' % (state[s][1], s) for s in sorted(state)]
repo.wwrite('.hgsubstate', ''.join(lines), '')
Matt Mackall
commit: recurse into subrepositories
r8813
Erik Zielke
subrepo: make update -C clean the working directory for svn subrepos...
r13322 def submerge(repo, wctx, mctx, actx, overwrite):
Mads Kiilerich
subrepo: docstrings
r11571 """delegated from merge.applyupdates: merging of .hgsubstate file
in working context, merging context and ancestor context"""
Matt Mackall
subrepo: add update/merge logic
r8814 if mctx == actx: # backwards?
actx = wctx.p1()
s1 = wctx.substate
s2 = mctx.substate
sa = actx.substate
sm = {}
Matt Mackall
subrepo: add more debugging output, lose _ markers
r9782 repo.ui.debug("subrepo merge %s %s %s\n" % (wctx, mctx, actx))
Matt Mackall
subrepo: add some debug output to submerge
r9779 def debug(s, msg, r=""):
if r:
Augie Fackler
subrepo: add table-based dispatch for subrepo types
r10177 r = "%s:%s:%s" % r
Matt Mackall
subrepo: add more debugging output, lose _ markers
r9782 repo.ui.debug(" subrepo %s: %s %s\n" % (s, msg, r))
Matt Mackall
subrepo: add some debug output to submerge
r9779
Matt Mackall
subrepo: add update/merge logic
r8814 for s, l in s1.items():
Matt Mackall
subrepo: correctly handle update -C with modified subrepos (issue2022)...
r11470 a = sa.get(s, nullstate)
Matt Mackall
subrepo: fix recording of + in .hgsubstate (issue2217)
r11463 ld = l # local state with possible dirty flag for compares
Matt Mackall
subrepo: correctly handle update -C with modified subrepos (issue2022)...
r11470 if wctx.sub(s).dirty():
Matt Mackall
subrepo: fix recording of + in .hgsubstate (issue2217)
r11463 ld = (l[0], l[1] + "+")
Matt Mackall
subrepo: correctly handle update -C with modified subrepos (issue2022)...
r11470 if wctx == actx: # overwrite
a = ld
Matt Mackall
subrepo: fix recording of + in .hgsubstate (issue2217)
r11463
Matt Mackall
subrepo: add update/merge logic
r8814 if s in s2:
r = s2[s]
Matt Mackall
subrepo: fix recording of + in .hgsubstate (issue2217)
r11463 if ld == r or r == a: # no change or local is newer
Matt Mackall
subrepo: add update/merge logic
r8814 sm[s] = l
continue
Matt Mackall
subrepo: fix recording of + in .hgsubstate (issue2217)
r11463 elif ld == a: # other side changed
Matt Mackall
subrepo: add more debugging output, lose _ markers
r9782 debug(s, "other changed, get", r)
Erik Zielke
subrepo: make update -C clean the working directory for svn subrepos...
r13322 wctx.sub(s).get(r, overwrite)
Matt Mackall
subrepo: add update/merge logic
r8814 sm[s] = r
Matt Mackall
subrepo: fix recording of + in .hgsubstate (issue2217)
r11463 elif ld[0] != r[0]: # sources differ
Simon Heimberg
ui: extract choice from prompt...
r9048 if repo.ui.promptchoice(
Matt Mackall
subrepo: add update/merge logic
r8814 _(' subrepository sources for %s differ\n'
Dongsheng Song
Fix warning: Seen unexpected token "%"
r8908 'use (l)ocal source (%s) or (r)emote source (%s)?')
Matt Mackall
subrepo: add update/merge logic
r8814 % (s, l[0], r[0]),
Simon Heimberg
ui: extract choice from prompt...
r9048 (_('&Local'), _('&Remote')), 0):
Matt Mackall
subrepo: add more debugging output, lose _ markers
r9782 debug(s, "prompt changed, get", r)
Erik Zielke
subrepo: make update -C clean the working directory for svn subrepos...
r13322 wctx.sub(s).get(r, overwrite)
Matt Mackall
subrepo: add update/merge logic
r8814 sm[s] = r
Matt Mackall
subrepo: fix recording of + in .hgsubstate (issue2217)
r11463 elif ld[1] == a[1]: # local side is unchanged
Matt Mackall
subrepo: add more debugging output, lose _ markers
r9782 debug(s, "other side changed, get", r)
Erik Zielke
subrepo: make update -C clean the working directory for svn subrepos...
r13322 wctx.sub(s).get(r, overwrite)
Matt Mackall
subrepo: add update/merge logic
r8814 sm[s] = r
else:
Matt Mackall
subrepo: add more debugging output, lose _ markers
r9782 debug(s, "both sides changed, merge with", r)
Matt Mackall
subrepo: add update/merge logic
r8814 wctx.sub(s).merge(r)
sm[s] = l
Matt Mackall
subrepo: fix recording of + in .hgsubstate (issue2217)
r11463 elif ld == a: # remote removed, local unchanged
Matt Mackall
subrepo: add more debugging output, lose _ markers
r9782 debug(s, "remote removed, remove")
Matt Mackall
subrepo: add update/merge logic
r8814 wctx.sub(s).remove()
Matt Mackall
subrepo: handle local added subrepo case correctly
r14417 elif a == nullstate: # not present in remote or ancestor
debug(s, "local added, keep")
sm[s] = l
continue
Matt Mackall
subrepo: add update/merge logic
r8814 else:
Simon Heimberg
ui: extract choice from prompt...
r9048 if repo.ui.promptchoice(
Matt Mackall
subrepo: add update/merge logic
r8814 _(' local changed subrepository %s which remote removed\n'
Dongsheng Song
Fix warning: Seen unexpected token "%"
r8908 'use (c)hanged version or (d)elete?') % s,
Martin Geisler
filemerge, subrepo: correct indention
r9049 (_('&Changed'), _('&Delete')), 0):
Matt Mackall
subrepo: add more debugging output, lose _ markers
r9782 debug(s, "prompt remove")
Matt Mackall
subrepo: add update/merge logic
r8814 wctx.sub(s).remove()
Adrian Buehlmann
subrepo: process merge substate in sorted order in submerge()...
r13857 for s, r in sorted(s2.items()):
Matt Mackall
subrepo: add update/merge logic
r8814 if s in s1:
continue
elif s not in sa:
Matt Mackall
subrepo: add more debugging output, lose _ markers
r9782 debug(s, "remote added, get", r)
Augie Fackler
subrepo: load from a context where the subrepo exists
r10175 mctx.sub(s).get(r)
Matt Mackall
subrepo: add update/merge logic
r8814 sm[s] = r
elif r != sa[s]:
Simon Heimberg
ui: extract choice from prompt...
r9048 if repo.ui.promptchoice(
Matt Mackall
subrepo: add update/merge logic
r8814 _(' remote changed subrepository %s which local removed\n'
Dongsheng Song
Fix warning: Seen unexpected token "%"
r8908 'use (c)hanged version or (d)elete?') % s,
Martin Geisler
filemerge, subrepo: correct indention
r9049 (_('&Changed'), _('&Delete')), 0) == 0:
Matt Mackall
subrepo: add more debugging output, lose _ markers
r9782 debug(s, "prompt recreate", r)
Matt Mackall
subrepo: add update/merge logic
r8814 wctx.sub(s).get(r)
sm[s] = r
# record merged .hgsubstate
writestate(repo, sm)
Erik Zielke
subrepos: prompt on conflicts on update with dirty subrepos...
r13417 def _updateprompt(ui, sub, dirty, local, remote):
if dirty:
msg = (_(' subrepository sources for %s differ\n'
'use (l)ocal source (%s) or (r)emote source (%s)?\n')
% (subrelpath(sub), local, remote))
else:
msg = (_(' subrepository sources for %s differ (in checked out version)\n'
'use (l)ocal source (%s) or (r)emote source (%s)?\n')
% (subrelpath(sub), local, remote))
return ui.promptchoice(msg, (_('&Local'), _('&Remote')), 0)
Mads Kiilerich
subrepo: rename relpath to subrelpath and introduce reporelpath
r12752 def reporelpath(repo):
"""return path to this (sub)repo as seen from outermost repo"""
parent = repo
Augie Fackler
subrepo: use safehasattr instead of hasattr...
r14963 while util.safehasattr(parent, '_subparent'):
Mads Kiilerich
subrepo: rename relpath to subrelpath and introduce reporelpath
r12752 parent = parent._subparent
Matt Mackall
subrepo: fix repo relative path calculation for root directories (issue3033)
r15191 p = parent.root.rstrip(os.sep)
return repo.root[len(p) + 1:]
Mads Kiilerich
subrepo: rename relpath to subrelpath and introduce reporelpath
r12752
def subrelpath(sub):
Mads Kiilerich
subrepo: docstrings
r11571 """return path to this subrepo as seen from outermost repo"""
Augie Fackler
subrepo: use safehasattr instead of hasattr...
r14963 if util.safehasattr(sub, '_relpath'):
Eric Eisner
subrepo: fix subrelpath for git subrepos...
r13181 return sub._relpath
Augie Fackler
subrepo: use safehasattr instead of hasattr...
r14963 if not util.safehasattr(sub, '_repo'):
Edouard Gomez
subrepo: print paths relative to upper repo root for push/pull/commit...
r11112 return sub._path
Mads Kiilerich
subrepo: rename relpath to subrelpath and introduce reporelpath
r12752 return reporelpath(sub._repo)
Edouard Gomez
subrepo: print paths relative to upper repo root for push/pull/commit...
r11112
Mads Kiilerich
subrepo: abort instead of pushing/pulling to the repo itself...
r12753 def _abssource(repo, push=False, abort=True):
"""return pull/push path of repo - either based on parent repo .hgsub info
or on the top repo config. Abort or return None if no source found."""
Augie Fackler
subrepo: use safehasattr instead of hasattr...
r14963 if util.safehasattr(repo, '_subparent'):
Brodie Rao
url: move URL parsing functions into util to improve startup time...
r14076 source = util.url(repo._subsource)
Matt Mackall
subrepos: be smarter about what's an absolute path (issue2808)
r14766 if source.isabs():
return str(source)
Brodie Rao
subrepos: use url.url when normalizing repo paths...
r13771 source.path = posixpath.normpath(source.path)
Mads Kiilerich
subrepo: abort instead of pushing/pulling to the repo itself...
r12753 parent = _abssource(repo._subparent, push, abort=False)
if parent:
Brodie Rao
url: move URL parsing functions into util to improve startup time...
r14076 parent = util.url(parent)
Mads Kiilerich
subrepo: fix cloning of repos from urls without slash after host (issue2970)...
r15055 parent.path = posixpath.join(parent.path or '', source.path)
Brodie Rao
subrepos: use url.url when normalizing repo paths...
r13771 parent.path = posixpath.normpath(parent.path)
return str(parent)
Mads Kiilerich
subrepo: abort instead of pushing/pulling to the repo itself...
r12753 else: # recursion reached top repo
Augie Fackler
subrepo: use safehasattr instead of hasattr...
r14963 if util.safehasattr(repo, '_subtoppath'):
Mads Kiilerich
subrepo: propagate non-default pull/push path to relative subrepos (issue1852)
r12852 return repo._subtoppath
Mads Kiilerich
subrepo: abort instead of pushing/pulling to the repo itself...
r12753 if push and repo.ui.config('paths', 'default-push'):
return repo.ui.config('paths', 'default-push')
if repo.ui.config('paths', 'default'):
return repo.ui.config('paths', 'default')
if abort:
Martin Geisler
check-code: find trailing whitespace
r12770 raise util.Abort(_("default path for subrepository %s not found") %
Mads Kiilerich
subrepo: abort instead of pushing/pulling to the repo itself...
r12753 reporelpath(repo))
Matt Mackall
subrepo: add update/merge logic
r8814
Martin Geisler
subrepos: add function for iterating over ctx subrepos
r12176 def itersubrepos(ctx1, ctx2):
"""find subrepos in ctx1 or ctx2"""
# Create a (subpath, ctx) mapping where we prefer subpaths from
# ctx1. The subpaths from ctx2 are important when the .hgsub file
# has been modified (in ctx2) but not yet committed (in ctx1).
subpaths = dict.fromkeys(ctx2.substate, ctx2)
subpaths.update(dict.fromkeys(ctx1.substate, ctx1))
for subpath, ctx in sorted(subpaths.iteritems()):
yield subpath, ctx.sub(subpath)
Matt Mackall
commit: recurse into subrepositories
r8813 def subrepo(ctx, path):
Mads Kiilerich
subrepo: docstrings
r11571 """return instance of the right subrepo class for subrepo in path"""
Matt Mackall
commit: recurse into subrepositories
r8813 # subrepo inherently violates our import layering rules
# because it wants to make repo objects from deep inside the stack
# so we manually delay the circular imports to not break
# scripts that don't use our demand-loading
Abderrahim Kitouni
subrepo: use hg.repository instead of creating localrepo directly...
r9092 global hg
import hg as h
Matt Mackall
subrepo: add update/merge logic
r8814 hg = h
Matt Mackall
commit: recurse into subrepositories
r8813
Adrian Buehlmann
rename path_auditor to pathauditor...
r14220 scmutil.pathauditor(ctx._repo.root)(path)
Matt Mackall
commit: recurse into subrepositories
r8813 state = ctx.substate.get(path, nullstate)
Augie Fackler
subrepo: add table-based dispatch for subrepo types
r10177 if state[2] not in types:
Benoit Boissinot
subrepo: fix errors reported by pylint
r10299 raise util.Abort(_('unknown subrepo type %s') % state[2])
Augie Fackler
subrepo: add table-based dispatch for subrepo types
r10177 return types[state[2]](ctx, path, state[:2])
Matt Mackall
commit: recurse into subrepositories
r8813
Martin Geisler
subrepo: add abstract superclass for subrepo classes
r11559 # subrepo classes need to implement the following abstract class:
class abstractsubrepo(object):
Kevin Bullock
mq: update .hgsubstate if subrepos are clean (issue2499)...
r13174 def dirty(self, ignoreupdate=False):
"""returns true if the dirstate of the subrepo is dirty or does not
match current stored state. If ignoreupdate is true, only check
whether the subrepo has uncommitted changes in its dirstate.
Martin Geisler
subrepo: add abstract superclass for subrepo classes
r11559 """
raise NotImplementedError
Brodie Rao
subrepos: add missing self argument to abstractsubrepo.checknested
r12506 def checknested(self, path):
Martin Geisler
localrepo: add auditor attribute which knows about subrepos
r12162 """check if path is a subrepository within this repository"""
return False
Martin Geisler
subrepo: add abstract superclass for subrepo classes
r11559 def commit(self, text, user, date):
"""commit the current changes to the subrepo with the given
log message. Use given user and date if possible. Return the
new state of the subrepo.
"""
raise NotImplementedError
def remove(self):
"""remove the subrepo
Matt Mackall
commit: recurse into subrepositories
r8813
Martin Geisler
subrepo: add abstract superclass for subrepo classes
r11559 (should verify the dirstate is not dirty first)
"""
raise NotImplementedError
Erik Zielke
subrepo: make update -C clean the working directory for svn subrepos...
r13322 def get(self, state, overwrite=False):
Martin Geisler
subrepo: add abstract superclass for subrepo classes
r11559 """run whatever commands are needed to put the subrepo into
this state
"""
raise NotImplementedError
Erik Zielke
subrepo: remove argument introduced by mistake in c19b9282d3a7
r13413 def merge(self, state):
Martin Geisler
subrepo: add abstract superclass for subrepo classes
r11559 """merge currently-saved state with the new state."""
raise NotImplementedError
def push(self, force):
Martin Geisler
Merge with stable
r11572 """perform whatever action is analogous to 'hg push'
Martin Geisler
subrepo: add abstract superclass for subrepo classes
r11559
This may be a no-op on some systems.
"""
raise NotImplementedError
Martin Geisler
add: recurse into subrepositories with --subrepos/-S flag
r12270 def add(self, ui, match, dryrun, prefix):
return []
Martin Geisler
subrepo: add abstract superclass for subrepo classes
r11559
Martin Geisler
status: recurse into subrepositories with --subrepos/-S flag
r12166 def status(self, rev2, **opts):
return [], [], [], [], [], [], []
Martin Geisler
diff: recurse into subrepositories with --subrepos/-S flag
r12167 def diff(self, diffopts, node2, match, prefix, **opts):
pass
Martin Geisler
outgoing: recurse into subrepositories with --subrepos/-S flag...
r12272 def outgoing(self, ui, dest, opts):
return 1
Martin Geisler
incoming: recurse into subrepositories with --subrepos/-S flag...
r12274 def incoming(self, ui, source, opts):
return 1
Martin Geisler
subrepo: introduce files and filedata methods for subrepo classes
r12322 def files(self):
"""return filename iterator"""
raise NotImplementedError
def filedata(self, name):
"""return file data"""
raise NotImplementedError
def fileflags(self, name):
"""return file flags"""
return ''
Martin Geisler
subrepo: add progress bar support to archive
r13144 def archive(self, ui, archiver, prefix):
files = self.files()
total = len(files)
relpath = subrelpath(self)
ui.progress(_('archiving (%s)') % relpath, 0,
unit=_('files'), total=total)
for i, name in enumerate(files):
Martin Geisler
subrepo: add support for 'hg archive'
r12323 flags = self.fileflags(name)
mode = 'x' in flags and 0755 or 0644
symlink = 'l' in flags
archiver.addfile(os.path.join(prefix, self._path, name),
mode, symlink, self.filedata(name))
Martin Geisler
subrepo: add progress bar support to archive
r13144 ui.progress(_('archiving (%s)') % relpath, i + 1,
unit=_('files'), total=total)
ui.progress(_('archiving (%s)') % relpath, None)
Martin Geisler
subrepo: add support for 'hg archive'
r12323
Martin Geisler
subrepo: add abstract superclass for subrepo classes
r11559 class hgsubrepo(abstractsubrepo):
Matt Mackall
commit: recurse into subrepositories
r8813 def __init__(self, ctx, path, state):
self._path = path
self._state = state
r = ctx._repo
root = r.wjoin(path)
Benoit Boissinot
subrepo: keep ui and hgrc in sync when creating new repo
r10666 create = False
if not os.path.exists(os.path.join(root, '.hg')):
create = True
Matt Mackall
subrepo: add update/merge logic
r8814 util.makedirs(root)
Benoit Boissinot
subrepo: keep ui and hgrc in sync when creating new repo
r10666 self._repo = hg.repository(r.ui, root, create=create)
Martin Geisler
subrepo: create subrepos using clone instead of pull...
r14281 self._initrepo(r, state[0], create)
def _initrepo(self, parentrepo, source, create):
self._repo._subparent = parentrepo
self._repo._subsource = source
Matt Mackall
commit: recurse into subrepositories
r8813
Benoit Boissinot
subrepo: keep ui and hgrc in sync when creating new repo
r10666 if create:
fp = self._repo.opener("hgrc", "w", text=True)
fp.write('[paths]\n')
def addpathconfig(key, value):
Mads Kiilerich
subrepo: abort instead of pushing/pulling to the repo itself...
r12753 if value:
fp.write('%s = %s\n' % (key, value))
self._repo.ui.setconfig('paths', key, value)
Benoit Boissinot
subrepo: keep ui and hgrc in sync when creating new repo
r10666
Mads Kiilerich
subrepo: abort instead of pushing/pulling to the repo itself...
r12753 defpath = _abssource(self._repo, abort=False)
defpushpath = _abssource(self._repo, True, abort=False)
Benoit Boissinot
subrepo: keep ui and hgrc in sync when creating new repo
r10666 addpathconfig('default', defpath)
Edouard Gomez
subrepo: fix hgrc paths section during subrepo pulling...
r10697 if defpath != defpushpath:
addpathconfig('default-push', defpushpath)
Benoit Boissinot
subrepo: keep ui and hgrc in sync when creating new repo
r10666 fp.close()
Martin Geisler
add: recurse into subrepositories with --subrepos/-S flag
r12270 def add(self, ui, match, dryrun, prefix):
return cmdutil.add(ui, self._repo, match, dryrun, True,
os.path.join(prefix, self._path))
Martin Geisler
status: recurse into subrepositories with --subrepos/-S flag
r12166 def status(self, rev2, **opts):
try:
rev1 = self._state[1]
ctx1 = self._repo[rev1]
ctx2 = self._repo[rev2]
return self._repo.status(ctx1, ctx2, **opts)
except error.RepoLookupError, inst:
Wagner Bruna
subrepo: improve lookup error messages
r12503 self._repo.ui.warn(_('warning: error "%s" in subrepository "%s"\n')
Mads Kiilerich
subrepo: rename relpath to subrelpath and introduce reporelpath
r12752 % (inst, subrelpath(self)))
Martin Geisler
status: recurse into subrepositories with --subrepos/-S flag
r12166 return [], [], [], [], [], [], []
Martin Geisler
diff: recurse into subrepositories with --subrepos/-S flag
r12167 def diff(self, diffopts, node2, match, prefix, **opts):
try:
node1 = node.bin(self._state[1])
Patrick Mezard
subrepos: handle diff nodeids in subrepos, not before...
r12209 # We currently expect node2 to come from substate and be
# in hex format
Martin Geisler
subrepo: handle diff with working copy...
r12210 if node2 is not None:
node2 = node.bin(node2)
Martin Geisler
diff: recurse into subrepositories with --subrepos/-S flag
r12167 cmdutil.diffordiffstat(self._repo.ui, self._repo, diffopts,
node1, node2, match,
prefix=os.path.join(prefix, self._path),
listsubrepos=True, **opts)
except error.RepoLookupError, inst:
Wagner Bruna
subrepo: improve lookup error messages
r12503 self._repo.ui.warn(_('warning: error "%s" in subrepository "%s"\n')
Mads Kiilerich
subrepo: rename relpath to subrelpath and introduce reporelpath
r12752 % (inst, subrelpath(self)))
Martin Geisler
diff: recurse into subrepositories with --subrepos/-S flag
r12167
Martin Geisler
subrepo: add progress bar support to archive
r13144 def archive(self, ui, archiver, prefix):
Martin Geisler
subrepo: pull revisions on demand when archiving hg subrepos...
r15286 self._get(self._state + ('hg',))
Martin Geisler
subrepo: add progress bar support to archive
r13144 abstractsubrepo.archive(self, ui, archiver, prefix)
Martin Geisler
subrepo: add support for 'hg archive'
r12323
rev = self._state[1]
ctx = self._repo[rev]
for subpath in ctx.substate:
s = subrepo(ctx, subpath)
Martin Geisler
subrepo: add progress bar support to archive
r13144 s.archive(ui, archiver, os.path.join(prefix, self._path))
Martin Geisler
subrepo: add support for 'hg archive'
r12323
Kevin Bullock
mq: update .hgsubstate if subrepos are clean (issue2499)...
r13174 def dirty(self, ignoreupdate=False):
Matt Mackall
commit: recurse into subrepositories
r8813 r = self._state[1]
Kevin Bullock
mq: update .hgsubstate if subrepos are clean (issue2499)...
r13174 if r == '' and not ignoreupdate: # no state recorded
Matt Mackall
commit: recurse into subrepositories
r8813 return True
w = self._repo[None]
Matt Mackall
extensions: drop maxlength from enabled and disabled...
r14316 if r != w.p1().hex() and not ignoreupdate:
Kevin Bullock
subrepo: clarify comments in dirty() methods...
r13325 # different version checked out
Matt Mackall
commit: recurse into subrepositories
r8813 return True
return w.dirty() # working directory changed
Martin Geisler
localrepo: add auditor attribute which knows about subrepos
r12162 def checknested(self, path):
return self._repo._checknested(self._repo.wjoin(path))
Matt Mackall
commit: recurse into subrepositories
r8813 def commit(self, text, user, date):
Kevin Bullock
subrepo: don't commit in subrepo if it's clean...
r14898 # don't bother committing in the subrepo if it's only been
# updated
if not self.dirty(True):
return self._repo['.'].hex()
Mads Kiilerich
subrepo: rename relpath to subrelpath and introduce reporelpath
r12752 self._repo.ui.debug("committing subrepo %s\n" % subrelpath(self))
Matt Mackall
commit: recurse into subrepositories
r8813 n = self._repo.commit(text, user, date)
if not n:
return self._repo['.'].hex() # different version checked out
return node.hex(n)
Matt Mackall
subrepo: add update/merge logic
r8814
def remove(self):
# we can't fully delete the repository as it may contain
# local-only history
Mads Kiilerich
subrepo: rename relpath to subrelpath and introduce reporelpath
r12752 self._repo.ui.note(_('removing subrepo %s\n') % subrelpath(self))
Matt Mackall
subrepo: add update/merge logic
r8814 hg.clean(self._repo, node.nullid, False)
Matt Mackall
subrepo: add auto-pull for merge
r9507 def _get(self, state):
Augie Fackler
subrepo: add table-based dispatch for subrepo types
r10177 source, revision, kind = state
Martin Geisler
subrepo: simplify hgsubrepo._get a little
r13753 if revision not in self._repo:
Matt Mackall
subrepo: add update/merge logic
r8814 self._repo._subsource = source
srcurl = _abssource(self._repo)
Matt Mackall
hg: change various repository() users to use peer() where appropriate...
r14556 other = hg.peer(self._repo.ui, {}, srcurl)
Martin Geisler
subrepo: create subrepos using clone instead of pull...
r14281 if len(self._repo) == 0:
self._repo.ui.status(_('cloning subrepo %s from %s\n')
% (subrelpath(self), srcurl))
parentrepo = self._repo._subparent
Martin Geisler
subrepo: abort in hgsubrepo._get if the destination is obstructed...
r15287 shutil.rmtree(self._repo.path)
Peter Arrenbrecht
hg: add opts argument to clone for internal remoteui
r14553 other, self._repo = hg.clone(self._repo._subparent.ui, {}, other,
self._repo.root, update=False)
Martin Geisler
subrepo: create subrepos using clone instead of pull...
r14281 self._initrepo(parentrepo, source, create=True)
else:
self._repo.ui.status(_('pulling subrepo %s from %s\n')
% (subrelpath(self), srcurl))
self._repo.pull(other)
David Soria Parra
bookmarks: separate bookmarks update code from localrepo's pull....
r13646 bookmarks.updatefromremote(self._repo.ui, self._repo, other)
Matt Mackall
subrepo: add update/merge logic
r8814
Erik Zielke
subrepo: make update -C clean the working directory for svn subrepos...
r13322 def get(self, state, overwrite=False):
Matt Mackall
subrepo: add auto-pull for merge
r9507 self._get(state)
Augie Fackler
subrepo: add table-based dispatch for subrepo types
r10177 source, revision, kind = state
Matt Mackall
subrepo: add more debugging output, lose _ markers
r9782 self._repo.ui.debug("getting subrepo %s\n" % self._path)
Matt Mackall
subrepo: add update/merge logic
r8814 hg.clean(self._repo, revision, False)
def merge(self, state):
Matt Mackall
subrepo: add auto-pull for merge
r9507 self._get(state)
Matt Mackall
subrepo: do a linear update when appropriate
r9781 cur = self._repo['.']
dst = self._repo[state[1]]
Benoit Boissinot
subrepo: fix merging of already merged subrepos (issue1986)...
r10251 anc = dst.ancestor(cur)
Erik Zielke
subrepos: prompt on conflicts on update with dirty subrepos...
r13417
def mergefunc():
if anc == cur:
self._repo.ui.debug("updating subrepo %s\n" % subrelpath(self))
hg.update(self._repo, state[1])
elif anc == dst:
self._repo.ui.debug("skipping subrepo %s\n" % subrelpath(self))
else:
self._repo.ui.debug("merging subrepo %s\n" % subrelpath(self))
hg.merge(self._repo, state[1], remind=False)
wctx = self._repo[None]
if self.dirty():
if anc != dst:
if _updateprompt(self._repo.ui, self, wctx.dirty(), cur, dst):
mergefunc()
else:
mergefunc()
Matt Mackall
subrepo: do a linear update when appropriate
r9781 else:
Erik Zielke
subrepos: prompt on conflicts on update with dirty subrepos...
r13417 mergefunc()
Matt Mackall
subrepo: basic push support
r8815
def push(self, force):
# push subrepos depth-first for coherent ordering
c = self._repo['']
subs = c.substate # only repos that are committed
for s in sorted(subs):
Matt Mackall
subrepo: propagate and catch push failures
r11067 if not c.sub(s).push(force):
return False
Matt Mackall
subrepo: basic push support
r8815
dsturl = _abssource(self._repo, True)
Edouard Gomez
subrepo: print pushing url
r11111 self._repo.ui.status(_('pushing subrepo %s to %s\n') %
Mads Kiilerich
subrepo: rename relpath to subrelpath and introduce reporelpath
r12752 (subrelpath(self), dsturl))
Matt Mackall
hg: change various repository() users to use peer() where appropriate...
r14556 other = hg.peer(self._repo.ui, {}, dsturl)
Matt Mackall
subrepo: propagate and catch push failures
r11067 return self._repo.push(other, force)
Augie Fackler
subrepo: add table-based dispatch for subrepo types
r10177
Martin Geisler
outgoing: recurse into subrepositories with --subrepos/-S flag...
r12272 def outgoing(self, ui, dest, opts):
return hg.outgoing(ui, self._repo, _abssource(self._repo, True), opts)
Martin Geisler
incoming: recurse into subrepositories with --subrepos/-S flag...
r12274 def incoming(self, ui, source, opts):
return hg.incoming(ui, self._repo, _abssource(self._repo, False), opts)
Martin Geisler
subrepo: introduce files and filedata methods for subrepo classes
r12322 def files(self):
rev = self._state[1]
ctx = self._repo[rev]
return ctx.manifest()
def filedata(self, name):
rev = self._state[1]
return self._repo[rev][name].data()
def fileflags(self, name):
rev = self._state[1]
ctx = self._repo[rev]
return ctx.flags(name)
Martin Geisler
subrepo: add abstract superclass for subrepo classes
r11559 class svnsubrepo(abstractsubrepo):
Augie Fackler
subrepo: Subversion support
r10178 def __init__(self, ctx, path, state):
self._path = path
self._state = state
self._ctx = ctx
self._ui = ctx._repo.ui
Matt Mackall
subrepo: improve error message when svn isn't found...
r15190 self._exe = util.findexe('svn')
if not self._exe:
raise util.Abort(_("'svn' executable not found for subrepo '%s'")
% self._path)
Augie Fackler
subrepo: Subversion support
r10178
Augie Fackler
svn subrepos: work around checkout obstructions (issue2752)...
r14664 def _svncommand(self, commands, filename='', failok=False):
Matt Mackall
subrepo: improve error message when svn isn't found...
r15190 cmd = [self._exe]
Augie Fackler
subrepo: make stdin for svn a pipe for non-interactive use (issue2759)...
r14506 extrakw = {}
if not self._ui.interactive():
# Making stdin be a pipe should prevent svn from behaving
# interactively even if we can't pass --non-interactive.
extrakw['stdin'] = subprocess.PIPE
# Starting in svn 1.5 --non-interactive is a global flag
# instead of being per-command, but we need to support 1.4 so
# we have to be intelligent about what commands take
# --non-interactive.
if commands[0] in ('update', 'checkout', 'commit'):
cmd.append('--non-interactive')
Augie Fackler
subrepo: tell Subversion when we are non-interactive (issue2759)...
r14025 cmd.extend(commands)
Patrick Mezard
subrepo: handle svn tracked/unknown directory collisions...
r14050 if filename is not None:
path = os.path.join(self._ctx._repo.origroot, self._path, filename)
cmd.append(path)
Patrick Mezard
subrepo: force en_US.UTF-8 locale when calling svn...
r10199 env = dict(os.environ)
Patrick Mezard
subrepo: make svn use C locale for portability...
r10271 # Avoid localized output, preserve current locale for everything else.
env['LC_MESSAGES'] = 'C'
Eric Eisner
subrepo: use subprocess.Popen without the shell...
r13108 p = subprocess.Popen(cmd, bufsize=-1, close_fds=util.closefds,
Patrick Mezard
subrepo: use subprocess directly to avoid python 2.6 bug...
r13014 stdout=subprocess.PIPE, stderr=subprocess.PIPE,
Augie Fackler
subrepo: make stdin for svn a pipe for non-interactive use (issue2759)...
r14506 universal_newlines=True, env=env, **extrakw)
Patrick Mezard
subrepo: use subprocess directly to avoid python 2.6 bug...
r13014 stdout, stderr = p.communicate()
stderr = stderr.strip()
Augie Fackler
svn subrepos: work around checkout obstructions (issue2752)...
r14664 if not failok:
if p.returncode:
raise util.Abort(stderr or 'exited with code %d' % p.returncode)
if stderr:
self._ui.warn(stderr + '\n')
return stdout, stderr
Augie Fackler
subrepo: Subversion support
r10178
Patrick Mezard
subrepo: handle svn tracked/unknown directory collisions...
r14050 @propertycache
def _svnversion(self):
Augie Fackler
svn subrepos: work around checkout obstructions (issue2752)...
r14664 output, err = self._svncommand(['--version'], filename=None)
Patrick Mezard
subrepo: handle svn tracked/unknown directory collisions...
r14050 m = re.search(r'^svn,\s+version\s+(\d+)\.(\d+)', output)
if not m:
raise util.Abort(_('cannot retrieve svn tool version'))
return (int(m.group(1)), int(m.group(2)))
Patrick Mezard
subrepo: compare svn subrepo state to last committed revision...
r13287 def _wcrevs(self):
# Get the working directory revision as well as the last
# commit revision so we can compare the subrepo state with
# both. We used to store the working directory one.
Augie Fackler
svn subrepos: work around checkout obstructions (issue2752)...
r14664 output, err = self._svncommand(['info', '--xml'])
Patrick Mezard
subrepo: svn xml output is much easier to parse...
r10272 doc = xml.dom.minidom.parseString(output)
entries = doc.getElementsByTagName('entry')
Patrick Mezard
subrepo: compare svn subrepo state to last committed revision...
r13287 lastrev, rev = '0', '0'
if entries:
rev = str(entries[0].getAttribute('revision')) or '0'
commits = entries[0].getElementsByTagName('commit')
if commits:
lastrev = str(commits[0].getAttribute('revision')) or '0'
return (lastrev, rev)
def _wcrev(self):
return self._wcrevs()[0]
Augie Fackler
subrepo: Subversion support
r10178
Patrick Mezard
subrepo: handle svn externals and meta changes (issue1982)...
r10273 def _wcchanged(self):
"""Return (changes, extchanges) where changes is True
if the working directory was changed, and extchanges is
True if any of these changes concern an external entry.
"""
Augie Fackler
svn subrepos: work around checkout obstructions (issue2752)...
r14664 output, err = self._svncommand(['status', '--xml'])
Patrick Mezard
subrepo: handle svn externals and meta changes (issue1982)...
r10273 externals, changes = [], []
Patrick Mezard
subrepo: svn xml output is much easier to parse...
r10272 doc = xml.dom.minidom.parseString(output)
Patrick Mezard
subrepo: handle svn externals and meta changes (issue1982)...
r10273 for e in doc.getElementsByTagName('entry'):
s = e.getElementsByTagName('wc-status')
if not s:
continue
item = s[0].getAttribute('item')
props = s[0].getAttribute('props')
path = e.getAttribute('path')
if item == 'external':
externals.append(path)
if (item not in ('', 'normal', 'unversioned', 'external')
Vasily Titskiy
subrepo: handle adding svn subrepo with a svn:external file in it (issue2931)
r14994 or props not in ('', 'none', 'normal')):
Patrick Mezard
subrepo: handle svn externals and meta changes (issue1982)...
r10273 changes.append(path)
for path in changes:
for ext in externals:
if path == ext or path.startswith(ext + os.sep):
return True, True
return bool(changes), False
Augie Fackler
subrepo: Subversion support
r10178
Kevin Bullock
mq: update .hgsubstate if subrepos are clean (issue2499)...
r13174 def dirty(self, ignoreupdate=False):
if not self._wcchanged()[0]:
Patrick Mezard
Merge with stable
r13288 if self._state[1] in self._wcrevs() or ignoreupdate:
Kevin Bullock
mq: update .hgsubstate if subrepos are clean (issue2499)...
r13174 return False
Augie Fackler
subrepo: Subversion support
r10178 return True
def commit(self, text, user, date):
# user and date are out of our hands since svn is centralized
Patrick Mezard
subrepo: handle svn externals and meta changes (issue1982)...
r10273 changed, extchanged = self._wcchanged()
if not changed:
Augie Fackler
subrepo: Subversion support
r10178 return self._wcrev()
Patrick Mezard
subrepo: handle svn externals and meta changes (issue1982)...
r10273 if extchanged:
# Do not try to commit externals
raise util.Abort(_('cannot commit svn externals'))
Augie Fackler
svn subrepos: work around checkout obstructions (issue2752)...
r14664 commitinfo, err = self._svncommand(['commit', '-m', text])
Augie Fackler
subrepo: Subversion support
r10178 self._ui.status(commitinfo)
Martin Geisler
subrepo: use [0-9] instead of [\d] in svn subrepo regex...
r12060 newrev = re.search('Committed revision ([0-9]+).', commitinfo)
Augie Fackler
subrepo: Subversion support
r10178 if not newrev:
raise util.Abort(commitinfo.splitlines()[-1])
newrev = newrev.groups()[0]
Augie Fackler
svn subrepos: work around checkout obstructions (issue2752)...
r14664 self._ui.status(self._svncommand(['update', '-r', newrev])[0])
Augie Fackler
subrepo: Subversion support
r10178 return newrev
def remove(self):
if self.dirty():
Benoit Boissinot
subrepo: fix errors reported by pylint
r10299 self._ui.warn(_('not removing repo %s because '
'it has changes.\n' % self._path))
Augie Fackler
subrepo: Subversion support
r10178 return
Benoit Boissinot
i18n: mark more strings for translation
r10510 self._ui.note(_('removing subrepo %s\n') % self._path)
Patrick Mezard
subrepo: fix removing read-only svn files on Windows
r13013
def onerror(function, path, excinfo):
if function is not os.remove:
raise
# read-only files cannot be unlinked under Windows
s = os.stat(path)
if (s.st_mode & stat.S_IWRITE) != 0:
raise
os.chmod(path, stat.S_IMODE(s.st_mode) | stat.S_IWRITE)
os.remove(path)
Patrick Mezard
subrepo: prune empty directories when removing svn subrepo
r13015 path = self._ctx._repo.wjoin(self._path)
shutil.rmtree(path, onerror=onerror)
try:
os.removedirs(os.path.dirname(path))
except OSError:
pass
Augie Fackler
subrepo: Subversion support
r10178
Erik Zielke
subrepo: make update -C clean the working directory for svn subrepos...
r13322 def get(self, state, overwrite=False):
if overwrite:
Patrick Mezard
subrepo: fix update -C with svn subrepos when cwd != repo.root
r13332 self._svncommand(['revert', '--recursive'])
Patrick Mezard
subrepo: handle svn tracked/unknown directory collisions...
r14050 args = ['checkout']
if self._svnversion >= (1, 5):
args.append('--force')
Eli Carter
subrepo: correct revision in svn checkout...
r14820 # The revision must be specified at the end of the URL to properly
# update to a directory which has since been deleted and recreated.
args.append('%s@%s' % (state[0], state[1]))
Augie Fackler
svn subrepos: work around checkout obstructions (issue2752)...
r14664 status, err = self._svncommand(args, failok=True)
Martin Geisler
subrepo: use [0-9] instead of [\d] in svn subrepo regex...
r12060 if not re.search('Checked out revision [0-9]+.', status):
Augie Fackler
svn subrepos: work around checkout obstructions (issue2752)...
r14664 if ('is already a working copy for a different URL' in err
and (self._wcchanged() == (False, False))):
# obstructed but clean working copy, so just blow it away.
self.remove()
self.get(state, overwrite=False)
return
raise util.Abort((status or err).splitlines()[-1])
Augie Fackler
subrepo: Subversion support
r10178 self._ui.status(status)
def merge(self, state):
Erik Zielke
subrepos: prompt on conflicts on update with dirty subrepos...
r13417 old = self._state[1]
new = state[1]
if new != self._wcrev():
dirty = old == self._wcrev() or self._wcchanged()[0]
if _updateprompt(self._ui, self, dirty, self._wcrev(), new):
self.get(state, False)
Augie Fackler
subrepo: Subversion support
r10178
def push(self, force):
Matt Mackall
subrepo: fix silent push failure for SVN (issue2241)
r11455 # push is a no-op for SVN
return True
Augie Fackler
subrepo: Subversion support
r10178
Martin Geisler
subrepo: introduce files and filedata methods for subrepo classes
r12322 def files(self):
output = self._svncommand(['list'])
# This works because svn forbids \n in filenames.
return output.splitlines()
def filedata(self, name):
return self._svncommand(['cat'], name)
Eric Eisner
subrepo: gitsubrepo should inherit from abstractsubrepo
r13106 class gitsubrepo(abstractsubrepo):
Eric Eisner
subrepo: support for adding a git subrepo...
r12992 def __init__(self, ctx, path, state):
# TODO add git version check.
self._state = state
self._ctx = ctx
Eric Eisner
subrepo: fix subrelpath for git subrepos...
r13181 self._path = path
self._relpath = os.path.join(reporelpath(ctx._repo), path)
self._abspath = ctx._repo.wjoin(path)
Eric Eisner
subrepo: expand relative sources for git subrepos
r13460 self._subparent = ctx._repo
Eric Eisner
subrepo: support for adding a git subrepo...
r12992 self._ui = ctx._repo.ui
Eric Eisner
subrepo: use environment variable instead of git commit's --date...
r13095 def _gitcommand(self, commands, env=None, stream=False):
return self._gitdir(commands, env=env, stream=stream)[0]
Eric Eisner
subrepo: support for adding a git subrepo...
r12992
Eric Eisner
subrepo: use environment variable instead of git commit's --date...
r13095 def _gitdir(self, commands, env=None, stream=False):
Eric Eisner
subrepo: fix subrelpath for git subrepos...
r13181 return self._gitnodir(commands, env=env, stream=stream,
cwd=self._abspath)
Eric Eisner
subrepo: support for adding a git subrepo...
r12992
Eric Eisner
subrepo: use environment variable instead of git commit's --date...
r13095 def _gitnodir(self, commands, env=None, stream=False, cwd=None):
Eric Eisner
subrepo: support for adding a git subrepo...
r12992 """Calls the git command
The methods tries to call the git command. versions previor to 1.6.0
are not supported and very probably fail.
"""
Eric Eisner
subrepo: show git command with --debug...
r13110 self._ui.debug('%s: git %s\n' % (self._relpath, ' '.join(commands)))
Eric Eisner
subrepo: silence git output when ui.quiet is set
r13111 # unless ui.quiet is set, print git's stderr,
# which is mostly progress and useful info
errpipe = None
if self._ui.quiet:
errpipe = open(os.devnull, 'w')
Eric Eisner
subrepo: use subprocess.Popen without the shell...
r13108 p = subprocess.Popen(['git'] + commands, bufsize=-1, cwd=cwd, env=env,
Eric Eisner
subrepo: archive git subrepos
r13027 close_fds=util.closefds,
Eric Eisner
subrepo: silence git output when ui.quiet is set
r13111 stdout=subprocess.PIPE, stderr=errpipe)
Eric Eisner
subrepo: archive git subrepos
r13027 if stream:
return p.stdout, None
Eric Eisner
subrepo: strip gitcommand output
r13085 retdata = p.stdout.read().strip()
Eric Eisner
subrepo: support for adding a git subrepo...
r12992 # wait for the child to exit to avoid race condition.
p.wait()
Eric Eisner
subrepo: treat git error code 1 as success...
r13107 if p.returncode != 0 and p.returncode != 1:
Eric Eisner
subrepo: support for adding a git subrepo...
r12992 # there are certain error codes that are ok
Eric Eisner
subrepo: show git command with --debug...
r13110 command = commands[0]
Eric Eisner
subrepo: use low-level git-for-each-ref command in branchmap...
r13150 if command in ('cat-file', 'symbolic-ref'):
Eric Eisner
subrepo: support for adding a git subrepo...
r12992 return retdata, p.returncode
# for all others, abort
Eric Eisner
subrepo: show git command with --debug...
r13110 raise util.Abort('git %s error %d in %s' %
(command, p.returncode, self._relpath))
Eric Eisner
subrepo: support for adding a git subrepo...
r12992
return retdata, p.returncode
Eric Eisner
subrepo: don't crash when git repo is missing
r13553 def _gitmissing(self):
return not os.path.exists(os.path.join(self._abspath, '.git'))
Eric Eisner
subrepo: support for adding a git subrepo...
r12992 def _gitstate(self):
Eric Eisner
subrepo: strip gitcommand output
r13085 return self._gitcommand(['rev-parse', 'HEAD'])
Eric Eisner
subrepo: support for adding a git subrepo...
r12992
Eric Eisner
subrepo: defer determination of git's current branch
r13152 def _gitcurrentbranch(self):
current, err = self._gitdir(['symbolic-ref', 'HEAD', '--quiet'])
if err:
current = None
return current
Eric Eisner
subrepo: show the source that git pulls
r13569 def _gitremote(self, remote):
out = self._gitcommand(['remote', 'show', '-n', remote])
line = out.split('\n')[1]
i = line.index('URL: ') + len('URL: ')
return line[i:]
Eric Eisner
subrepo: support for adding a git subrepo...
r12992 def _githavelocally(self, revision):
out, code = self._gitdir(['cat-file', '-e', revision])
return code == 0
Eric Eisner
subrepo: lazier git push logic...
r13029 def _gitisancestor(self, r1, r2):
Eric Eisner
subrepo: strip gitcommand output
r13085 base = self._gitcommand(['merge-base', r1, r2])
Eric Eisner
subrepo: lazier git push logic...
r13029 return base == r1
Paul Molodowitch
subrepo: bare git repos considered dirty...
r14440 def _gitisbare(self):
return self._gitcommand(['config', '--bool', 'core.bare']) == 'true'
Eric Eisner
subrepo: update and merge works with any git branch
r12995 def _gitbranchmap(self):
Eric Eisner
subrepo: backout 519ac79d680b...
r13178 '''returns 2 things:
Eric Eisner
subrepo: return both mapping directions from gitbranchmap
r13086 a map from git branch to revision
Eric Eisner
subrepo: backout 519ac79d680b...
r13178 a map from revision to branches'''
Eric Eisner
subrepo: return both mapping directions from gitbranchmap
r13086 branch2rev = {}
rev2branch = {}
Eric Eisner
subrepo: backout 519ac79d680b...
r13178
Eric Eisner
subrepo: use low-level git-for-each-ref command in branchmap...
r13150 out = self._gitcommand(['for-each-ref', '--format',
Eric Eisner
subrepo: backout 519ac79d680b...
r13178 '%(objectname) %(refname)'])
Eric Eisner
subrepo: update and merge works with any git branch
r12995 for line in out.split('\n'):
Eric Eisner
subrepo: backout 519ac79d680b...
r13178 revision, ref = line.split(' ')
Eric Eisner
subrepo: disallow all unknown git ref types
r13465 if (not ref.startswith('refs/heads/') and
not ref.startswith('refs/remotes/')):
Eric Eisner
subrepo: update and merge works with any git branch
r12995 continue
Eric Eisner
subrepo: use low-level git-for-each-ref command in branchmap...
r13150 if ref.startswith('refs/remotes/') and ref.endswith('/HEAD'):
Eric Eisner
subrepo: update and merge works with any git branch
r12995 continue # ignore remote/HEAD redirects
Eric Eisner
subrepo: use low-level git-for-each-ref command in branchmap...
r13150 branch2rev[ref] = revision
rev2branch.setdefault(revision, []).append(ref)
Eric Eisner
subrepo: backout 519ac79d680b...
r13178 return branch2rev, rev2branch
def _gittracking(self, branches):
'return map of remote branch to local tracking branch'
# assumes no more than one local tracking branch for each remote
tracking = {}
for b in branches:
if b.startswith('refs/remotes/'):
continue
Eric Roshan Eisner
subrepo: fix git branch tracking logic (issue2920)
r15234 bname = b.split('/', 2)[2]
remote = self._gitcommand(['config', 'branch.%s.remote' % bname])
Eric Eisner
subrepo: backout 519ac79d680b...
r13178 if remote:
Eric Roshan Eisner
subrepo: fix git branch tracking logic (issue2920)
r15234 ref = self._gitcommand(['config', 'branch.%s.merge' % bname])
Eric Eisner
subrepo: backout 519ac79d680b...
r13178 tracking['refs/remotes/%s/%s' %
(remote, ref.split('/', 2)[2])] = b
return tracking
Eric Eisner
subrepo: lazily update git's local tracking branches...
r13087
Eric Eisner
subrepo: expand relative sources for git subrepos
r13460 def _abssource(self, source):
Eric Eisner
subrepo: recognize scp-style paths as git URLs
r13692 if '://' not in source:
# recognize the scp syntax as an absolute source
colon = source.find(':')
if colon != -1 and '/' not in source[:colon]:
return source
Eric Eisner
subrepo: expand relative sources for git subrepos
r13460 self._subsource = source
return _abssource(self)
Eric Eisner
subrepo: cloning and updating of git subrepos...
r12993 def _fetch(self, source, revision):
Eric Eisner
subrepo: don't crash when git repo is missing
r13553 if self._gitmissing():
Eric Eisner
subrepo: show the source that git clones
r13525 source = self._abssource(source)
self._ui.status(_('cloning subrepo %s from %s\n') %
(self._relpath, source))
self._gitnodir(['clone', source, self._abspath])
Eric Eisner
subrepo: cloning and updating of git subrepos...
r12993 if self._githavelocally(revision):
return
Eric Eisner
subrepo: show the source that git pulls
r13569 self._ui.status(_('pulling subrepo %s from %s\n') %
(self._relpath, self._gitremote('origin')))
Eric Eisner
subrepo: only attempt pulling from git's origin...
r13466 # try only origin: the originally cloned repo
Eric Eisner
subrepo: drop arguments unsupported by old git
r13097 self._gitcommand(['fetch'])
Eric Eisner
subrepo: cloning and updating of git subrepos...
r12993 if not self._githavelocally(revision):
raise util.Abort(_("revision %s does not exist in subrepo %s\n") %
Eric Eisner
subrepo: fix subrelpath for git subrepos...
r13181 (revision, self._relpath))
Eric Eisner
subrepo: cloning and updating of git subrepos...
r12993
Eric Eisner
subrepo: support ignoreupdate in gitsubrepo's dirty()
r13179 def dirty(self, ignoreupdate=False):
Eric Eisner
subrepo: don't crash when git repo is missing
r13553 if self._gitmissing():
Eric Eisner
subrepo: don't crash when git .hgsubstate is empty (issue2716)
r14469 return self._state[1] != ''
Paul Molodowitch
subrepo: bare git repos considered dirty...
r14440 if self._gitisbare():
return True
Eric Eisner
subrepo: support ignoreupdate in gitsubrepo's dirty()
r13179 if not ignoreupdate and self._state[1] != self._gitstate():
Kevin Bullock
subrepo: clarify comments in dirty() methods...
r13325 # different version checked out
Eric Eisner
subrepo: support for adding a git subrepo...
r12992 return True
# check for staged changes or modified files; ignore untracked files
Eric Eisner
subrepo: use low-level git-diff-index for dirty()...
r13153 out, code = self._gitdir(['diff-index', '--quiet', 'HEAD'])
return code == 1
Eric Eisner
subrepo: support for adding a git subrepo...
r12992
Martin Geisler
merge with stable
r13323 def get(self, state, overwrite=False):
Eric Eisner
subrepo: cloning and updating of git subrepos...
r12993 source, revision, kind = state
Eric Eisner
subrepo: don't crash when git .hgsubstate is empty (issue2716)
r14469 if not revision:
self.remove()
return
Eric Eisner
subrepo: cloning and updating of git subrepos...
r12993 self._fetch(source, revision)
Eric Eisner
subrepo: removing (and restoring) git subrepo state
r12996 # if the repo was set to be bare, unbare it
Paul Molodowitch
subrepo: bare git repos considered dirty...
r14440 if self._gitisbare():
Eric Eisner
subrepo: removing (and restoring) git subrepo state
r12996 self._gitcommand(['config', 'core.bare', 'false'])
if self._gitstate() == revision:
self._gitcommand(['reset', '--hard', 'HEAD'])
return
elif self._gitstate() == revision:
Erik Zielke
subrepo: make update -C clean the working directory for git subrepos...
r13324 if overwrite:
Augie Fackler
subrepo: trailing whitespace cleanup
r13927 # first reset the index to unmark new files for commit, because
Erik Zielke
subrepo: make update -C clean the working directory for git subrepos...
r13324 # reset --hard will otherwise throw away files added for commit,
# not just unmark them.
self._gitcommand(['reset', 'HEAD'])
self._gitcommand(['reset', '--hard', 'HEAD'])
Eric Eisner
subrepo: update and merge works with any git branch
r12995 return
Eric Eisner
subrepo: backout 519ac79d680b...
r13178 branch2rev, rev2branch = self._gitbranchmap()
Eric Eisner
subrepo: lazily update git's local tracking branches...
r13087
Erik Zielke
subrepo: make update -C clean the working directory for git subrepos...
r13324 def checkout(args):
cmd = ['checkout']
if overwrite:
# first reset the index to unmark new files for commit, because
# the -f option will otherwise throw away files added for
# commit, not just unmark them.
self._gitcommand(['reset', 'HEAD'])
cmd.append('-f')
self._gitcommand(cmd + args)
Eric Eisner
subrepo: lazily update git's local tracking branches...
r13087 def rawcheckout():
Eric Eisner
subrepo: update and merge works with any git branch
r12995 # no branch to checkout, check it out with no branch
Eric Eisner
subrepo: cloning and updating of git subrepos...
r12993 self._ui.warn(_('checking out detached HEAD in subrepo %s\n') %
self._relpath)
self._ui.warn(_('check out a git branch if you intend '
'to make changes\n'))
Erik Zielke
subrepo: make update -C clean the working directory for git subrepos...
r13324 checkout(['-q', revision])
Eric Eisner
subrepo: lazily update git's local tracking branches...
r13087
if revision not in rev2branch:
rawcheckout()
Eric Eisner
subrepo: update and merge works with any git branch
r12995 return
Eric Eisner
subrepo: return both mapping directions from gitbranchmap
r13086 branches = rev2branch[revision]
Eric Eisner
subrepo: update and merge works with any git branch
r12995 firstlocalbranch = None
for b in branches:
Eric Eisner
subrepo: use low-level git-for-each-ref command in branchmap...
r13150 if b == 'refs/heads/master':
Eric Eisner
subrepo: update and merge works with any git branch
r12995 # master trumps all other branches
Erik Zielke
subrepo: make update -C clean the working directory for git subrepos...
r13324 checkout(['refs/heads/master'])
Eric Eisner
subrepo: update and merge works with any git branch
r12995 return
Eric Eisner
subrepo: use low-level git-for-each-ref command in branchmap...
r13150 if not firstlocalbranch and not b.startswith('refs/remotes/'):
Eric Eisner
subrepo: update and merge works with any git branch
r12995 firstlocalbranch = b
if firstlocalbranch:
Erik Zielke
subrepo: make update -C clean the working directory for git subrepos...
r13324 checkout([firstlocalbranch])
Eric Eisner
subrepo: lazily update git's local tracking branches...
r13087 return
Eric Eisner
subrepo: backout 519ac79d680b...
r13178 tracking = self._gittracking(branch2rev.keys())
Eric Eisner
subrepo: lazily update git's local tracking branches...
r13087 # choose a remote branch already tracked if possible
remote = branches[0]
if remote not in tracking:
for b in branches:
if b in tracking:
remote = b
break
if remote not in tracking:
# create a new local tracking branch
Eric Eisner
subrepo: use low-level git-for-each-ref command in branchmap...
r13150 local = remote.split('/', 2)[2]
Erik Zielke
subrepo: make update -C clean the working directory for git subrepos...
r13324 checkout(['-b', local, remote])
Eric Eisner
subrepo: lazily update git's local tracking branches...
r13087 elif self._gitisancestor(branch2rev[tracking[remote]], remote):
# When updating to a tracked remote branch,
# if the local tracking branch is downstream of it,
# a normal `git pull` would have performed a "fast-forward merge"
# which is equivalent to updating the local branch to the remote.
# Since we are only looking at branching at update, we need to
# detect this situation and perform this action lazily.
Eric Eisner
subrepo: defer determination of git's current branch
r13152 if tracking[remote] != self._gitcurrentbranch():
Erik Zielke
subrepo: make update -C clean the working directory for git subrepos...
r13324 checkout([tracking[remote]])
Eric Eisner
subrepo: lazily update git's local tracking branches...
r13087 self._gitcommand(['merge', '--ff', remote])
else:
# a real merge would be required, just checkout the revision
rawcheckout()
Eric Eisner
subrepo: cloning and updating of git subrepos...
r12993
Eric Eisner
subrepo: support for adding a git subrepo...
r12992 def commit(self, text, user, date):
Eric Eisner
subrepo: don't crash when git repo is missing
r13553 if self._gitmissing():
raise util.Abort(_("subrepo %s is missing") % self._relpath)
Eric Eisner
subrepo: support for adding a git subrepo...
r12992 cmd = ['commit', '-a', '-m', text]
Eric Eisner
subrepo: use environment variable instead of git commit's --date...
r13095 env = os.environ.copy()
Eric Eisner
subrepo: support for adding a git subrepo...
r12992 if user:
cmd += ['--author', user]
if date:
# git's date parser silently ignores when seconds < 1e9
# convert to ISO8601
Eric Eisner
subrepo: use environment variable instead of git commit's --date...
r13095 env['GIT_AUTHOR_DATE'] = util.datestr(date,
'%Y-%m-%dT%H:%M:%S %1%2')
self._gitcommand(cmd, env=env)
Eric Eisner
subrepo: support for adding a git subrepo...
r12992 # make sure commit works otherwise HEAD might not exist under certain
# circumstances
return self._gitstate()
Eric Eisner
subrepo: allow git subrepos to push and merge...
r12994 def merge(self, state):
source, revision, kind = state
self._fetch(source, revision)
Eric Eisner
subrepo: strip gitcommand output
r13085 base = self._gitcommand(['merge-base', revision, self._state[1]])
Erik Zielke
subrepos: prompt on conflicts on update with dirty subrepos...
r13417 out, code = self._gitdir(['diff-index', '--quiet', 'HEAD'])
def mergefunc():
if base == revision:
self.get(state) # fast forward merge
elif base != self._state[1]:
self._gitcommand(['merge', '--no-commit', revision])
if self.dirty():
if self._gitstate() != revision:
dirty = self._gitstate() == self._state[1] or code != 0
Martin Geisler
subrepo: break long line found by check-code
r13432 if _updateprompt(self._ui, self, dirty,
self._state[1][:7], revision[:7]):
Erik Zielke
subrepos: prompt on conflicts on update with dirty subrepos...
r13417 mergefunc()
else:
mergefunc()
Eric Eisner
subrepo: allow git subrepos to push and merge...
r12994
def push(self, force):
Eric Eisner
subrepo: don't crash when git .hgsubstate is empty (issue2716)
r14469 if not self._state[1]:
return True
Eric Eisner
subrepo: don't crash when git repo is missing
r13553 if self._gitmissing():
raise util.Abort(_("subrepo %s is missing") % self._relpath)
Eric Eisner
subrepo: lazier git push logic...
r13029 # if a branch in origin contains the revision, nothing to do
Eric Eisner
subrepo: backout 519ac79d680b...
r13178 branch2rev, rev2branch = self._gitbranchmap()
Eric Eisner
subrepo: speed up git push logic...
r13109 if self._state[1] in rev2branch:
for b in rev2branch[self._state[1]]:
Eric Eisner
subrepo: use low-level git-for-each-ref command in branchmap...
r13150 if b.startswith('refs/remotes/origin/'):
Eric Eisner
subrepo: speed up git push logic...
r13109 return True
Eric Eisner
subrepo: return both mapping directions from gitbranchmap
r13086 for b, revision in branch2rev.iteritems():
Eric Eisner
subrepo: use low-level git-for-each-ref command in branchmap...
r13150 if b.startswith('refs/remotes/origin/'):
Eric Eisner
subrepo: return both mapping directions from gitbranchmap
r13086 if self._gitisancestor(self._state[1], revision):
return True
Eric Eisner
subrepo: lazier git push logic...
r13029 # otherwise, try to push the currently checked out branch
Eric Eisner
subrepo: allow git subrepos to push and merge...
r12994 cmd = ['push']
if force:
cmd.append('--force')
Eric Eisner
subrepo: defer determination of git's current branch
r13152
current = self._gitcurrentbranch()
Eric Eisner
subrepo: update and merge works with any git branch
r12995 if current:
Eric Eisner
subrepo: lazier git push logic...
r13029 # determine if the current branch is even useful
if not self._gitisancestor(self._state[1], current):
self._ui.warn(_('unrelated git branch checked out '
'in subrepo %s\n') % self._relpath)
return False
self._ui.status(_('pushing branch %s of subrepo %s\n') %
Eric Eisner
subrepo: use low-level git-for-each-ref command in branchmap...
r13150 (current.split('/', 2)[2], self._relpath))
Eric Eisner
subrepo: drop arguments unsupported by old git
r13097 self._gitcommand(cmd + ['origin', current])
Eric Eisner
subrepo: update and merge works with any git branch
r12995 return True
else:
self._ui.warn(_('no branch checked out in subrepo %s\n'
Eric Eisner
subrepo: lazier git push logic...
r13029 'cannot push revision %s') %
(self._relpath, self._state[1]))
Eric Eisner
subrepo: update and merge works with any git branch
r12995 return False
Eric Eisner
subrepo: allow git subrepos to push and merge...
r12994
Eric Eisner
subrepo: removing (and restoring) git subrepo state
r12996 def remove(self):
Eric Eisner
subrepo: don't crash when git repo is missing
r13553 if self._gitmissing():
return
Eric Eisner
subrepo: removing (and restoring) git subrepo state
r12996 if self.dirty():
self._ui.warn(_('not removing repo %s because '
Eric Eisner
subrepo: fix subrelpath for git subrepos...
r13181 'it has changes.\n') % self._relpath)
Eric Eisner
subrepo: removing (and restoring) git subrepo state
r12996 return
# we can't fully delete the repository as it may contain
# local-only history
Eric Eisner
subrepo: fix subrelpath for git subrepos...
r13181 self._ui.note(_('removing subrepo %s\n') % self._relpath)
Eric Eisner
subrepo: removing (and restoring) git subrepo state
r12996 self._gitcommand(['config', 'core.bare', 'true'])
Eric Eisner
subrepo: fix subrelpath for git subrepos...
r13181 for f in os.listdir(self._abspath):
Eric Eisner
subrepo: removing (and restoring) git subrepo state
r12996 if f == '.git':
continue
Eric Eisner
subrepo: fix subrelpath for git subrepos...
r13181 path = os.path.join(self._abspath, f)
Eric Eisner
subrepo: removing (and restoring) git subrepo state
r12996 if os.path.isdir(path) and not os.path.islink(path):
shutil.rmtree(path)
else:
os.remove(path)
Martin Geisler
subrepo: add progress bar support to archive
r13144 def archive(self, ui, archiver, prefix):
Eric Eisner
subrepo: archive git subrepos
r13027 source, revision = self._state
Eric Eisner
subrepo: don't crash when git .hgsubstate is empty (issue2716)
r14469 if not revision:
return
Eric Eisner
subrepo: archive git subrepos
r13027 self._fetch(source, revision)
# Parse git's native archive command.
# This should be much faster than manually traversing the trees
# and objects with many subprocess calls.
tarstream = self._gitcommand(['archive', revision], stream=True)
tar = tarfile.open(fileobj=tarstream, mode='r|')
Martin Geisler
subrepo: add progress bar support to archive
r13144 relpath = subrelpath(self)
ui.progress(_('archiving (%s)') % relpath, 0, unit=_('files'))
for i, info in enumerate(tar):
Eric Eisner
subrepo: fix git archive parsing of directories and symfiles
r13180 if info.isdir():
continue
if info.issym():
data = info.linkname
else:
data = tar.extractfile(info).read()
Eric Eisner
subrepo: fix subrelpath for git subrepos...
r13181 archiver.addfile(os.path.join(prefix, self._path, info.name),
Eric Eisner
subrepo: fix git archive parsing of directories and symfiles
r13180 info.mode, info.issym(), data)
Martin Geisler
subrepo: add progress bar support to archive
r13144 ui.progress(_('archiving (%s)') % relpath, i + 1,
unit=_('files'))
ui.progress(_('archiving (%s)') % relpath, None)
Eric Eisner
subrepo: archive git subrepos
r13027
Eric Eisner
subrepo: basic support for status of git subrepos
r13182 def status(self, rev2, **opts):
Eric Eisner
subrepo: don't crash when git .hgsubstate is empty (issue2716)
r14469 rev1 = self._state[1]
if self._gitmissing() or not rev1:
Eric Eisner
subrepo: don't crash when git repo is missing
r13553 # if the repo is missing, return no results
return [], [], [], [], [], [], []
Eric Eisner
subrepo: basic support for status of git subrepos
r13182 modified, added, removed = [], [], []
if rev2:
command = ['diff-tree', rev1, rev2]
else:
command = ['diff-index', rev1]
out = self._gitcommand(command)
for line in out.split('\n'):
tab = line.find('\t')
if tab == -1:
continue
status, f = line[tab - 1], line[tab + 1:]
if status == 'M':
modified.append(f)
elif status == 'A':
added.append(f)
elif status == 'D':
removed.append(f)
deleted = unknown = ignored = clean = []
return modified, added, removed, deleted, unknown, ignored, clean
Augie Fackler
subrepo: add table-based dispatch for subrepo types
r10177 types = {
'hg': hgsubrepo,
Augie Fackler
subrepo: Subversion support
r10178 'svn': svnsubrepo,
Eric Eisner
subrepo: support for adding a git subrepo...
r12992 'git': gitsubrepo,
Augie Fackler
subrepo: add table-based dispatch for subrepo types
r10177 }