67 |
67 |
68 class SubrepoAbort(error.Abort): |
68 class SubrepoAbort(error.Abort): |
69 """Exception class used to avoid handling a subrepo error more than once""" |
69 """Exception class used to avoid handling a subrepo error more than once""" |
70 |
70 |
71 def __init__(self, *args, **kw): |
71 def __init__(self, *args, **kw): |
72 self.subrepo = kw.pop(r'subrepo', None) |
72 self.subrepo = kw.pop('subrepo', None) |
73 self.cause = kw.pop(r'cause', None) |
73 self.cause = kw.pop('cause', None) |
74 error.Abort.__init__(self, *args, **kw) |
74 error.Abort.__init__(self, *args, **kw) |
75 |
75 |
76 |
76 |
77 def annotatesubrepoerror(func): |
77 def annotatesubrepoerror(func): |
78 def decoratedmethod(self, *args, **kargs): |
78 def decoratedmethod(self, *args, **kargs): |
967 # 1. if the no_backup is not set, revert all modified |
967 # 1. if the no_backup is not set, revert all modified |
968 # files inside the subrepo |
968 # files inside the subrepo |
969 # 2. update the subrepo to the revision specified in |
969 # 2. update the subrepo to the revision specified in |
970 # the corresponding substate dictionary |
970 # the corresponding substate dictionary |
971 self.ui.status(_(b'reverting subrepo %s\n') % substate[0]) |
971 self.ui.status(_(b'reverting subrepo %s\n') % substate[0]) |
972 if not opts.get(r'no_backup'): |
972 if not opts.get('no_backup'): |
973 # Revert all files on the subrepo, creating backups |
973 # Revert all files on the subrepo, creating backups |
974 # Note that this will not recursively revert subrepos |
974 # Note that this will not recursively revert subrepos |
975 # We could do it if there was a set:subrepos() predicate |
975 # We could do it if there was a set:subrepos() predicate |
976 opts = opts.copy() |
976 opts = opts.copy() |
977 opts[r'date'] = None |
977 opts['date'] = None |
978 opts[r'rev'] = substate[1] |
978 opts['rev'] = substate[1] |
979 |
979 |
980 self.filerevert(*pats, **opts) |
980 self.filerevert(*pats, **opts) |
981 |
981 |
982 # Update the repo to the revision specified in the given substate |
982 # Update the repo to the revision specified in the given substate |
983 if not opts.get(r'dry_run'): |
983 if not opts.get('dry_run'): |
984 self.get(substate, overwrite=True) |
984 self.get(substate, overwrite=True) |
985 |
985 |
986 def filerevert(self, *pats, **opts): |
986 def filerevert(self, *pats, **opts): |
987 ctx = self._repo[opts[r'rev']] |
987 ctx = self._repo[opts['rev']] |
988 parents = self._repo.dirstate.parents() |
988 parents = self._repo.dirstate.parents() |
989 if opts.get(r'all'): |
989 if opts.get('all'): |
990 pats = [b'set:modified()'] |
990 pats = [b'set:modified()'] |
991 else: |
991 else: |
992 pats = [] |
992 pats = [] |
993 cmdutil.revert(self.ui, self._repo, ctx, parents, *pats, **opts) |
993 cmdutil.revert(self.ui, self._repo, ctx, parents, *pats, **opts) |
994 |
994 |
1064 cmd = [self._exe] |
1064 cmd = [self._exe] |
1065 extrakw = {} |
1065 extrakw = {} |
1066 if not self.ui.interactive(): |
1066 if not self.ui.interactive(): |
1067 # Making stdin be a pipe should prevent svn from behaving |
1067 # Making stdin be a pipe should prevent svn from behaving |
1068 # interactively even if we can't pass --non-interactive. |
1068 # interactively even if we can't pass --non-interactive. |
1069 extrakw[r'stdin'] = subprocess.PIPE |
1069 extrakw['stdin'] = subprocess.PIPE |
1070 # Starting in svn 1.5 --non-interactive is a global flag |
1070 # Starting in svn 1.5 --non-interactive is a global flag |
1071 # instead of being per-command, but we need to support 1.4 so |
1071 # instead of being per-command, but we need to support 1.4 so |
1072 # we have to be intelligent about what commands take |
1072 # we have to be intelligent about what commands take |
1073 # --non-interactive. |
1073 # --non-interactive. |
1074 if commands[0] in (b'update', b'checkout', b'commit'): |
1074 if commands[0] in (b'update', b'checkout', b'commit'): |
1123 # Get the working directory revision as well as the last |
1123 # Get the working directory revision as well as the last |
1124 # commit revision so we can compare the subrepo state with |
1124 # commit revision so we can compare the subrepo state with |
1125 # both. We used to store the working directory one. |
1125 # both. We used to store the working directory one. |
1126 output, err = self._svncommand([b'info', b'--xml']) |
1126 output, err = self._svncommand([b'info', b'--xml']) |
1127 doc = xml.dom.minidom.parseString(output) |
1127 doc = xml.dom.minidom.parseString(output) |
1128 entries = doc.getElementsByTagName(r'entry') |
1128 entries = doc.getElementsByTagName('entry') |
1129 lastrev, rev = b'0', b'0' |
1129 lastrev, rev = b'0', b'0' |
1130 if entries: |
1130 if entries: |
1131 rev = pycompat.bytestr(entries[0].getAttribute(r'revision')) or b'0' |
1131 rev = pycompat.bytestr(entries[0].getAttribute('revision')) or b'0' |
1132 commits = entries[0].getElementsByTagName(r'commit') |
1132 commits = entries[0].getElementsByTagName('commit') |
1133 if commits: |
1133 if commits: |
1134 lastrev = ( |
1134 lastrev = ( |
1135 pycompat.bytestr(commits[0].getAttribute(r'revision')) |
1135 pycompat.bytestr(commits[0].getAttribute('revision')) |
1136 or b'0' |
1136 or b'0' |
1137 ) |
1137 ) |
1138 return (lastrev, rev) |
1138 return (lastrev, rev) |
1139 |
1139 |
1140 def _wcrev(self): |
1140 def _wcrev(self): |
1147 is True if any change is a missing entry. |
1147 is True if any change is a missing entry. |
1148 """ |
1148 """ |
1149 output, err = self._svncommand([b'status', b'--xml']) |
1149 output, err = self._svncommand([b'status', b'--xml']) |
1150 externals, changes, missing = [], [], [] |
1150 externals, changes, missing = [], [], [] |
1151 doc = xml.dom.minidom.parseString(output) |
1151 doc = xml.dom.minidom.parseString(output) |
1152 for e in doc.getElementsByTagName(r'entry'): |
1152 for e in doc.getElementsByTagName('entry'): |
1153 s = e.getElementsByTagName(r'wc-status') |
1153 s = e.getElementsByTagName('wc-status') |
1154 if not s: |
1154 if not s: |
1155 continue |
1155 continue |
1156 item = s[0].getAttribute(r'item') |
1156 item = s[0].getAttribute('item') |
1157 props = s[0].getAttribute(r'props') |
1157 props = s[0].getAttribute('props') |
1158 path = e.getAttribute(r'path').encode('utf8') |
1158 path = e.getAttribute('path').encode('utf8') |
1159 if item == r'external': |
1159 if item == 'external': |
1160 externals.append(path) |
1160 externals.append(path) |
1161 elif item == r'missing': |
1161 elif item == 'missing': |
1162 missing.append(path) |
1162 missing.append(path) |
1163 if item not in ( |
1163 if item not in ( |
1164 r'', |
1164 '', |
1165 r'normal', |
1165 'normal', |
1166 r'unversioned', |
1166 'unversioned', |
1167 r'external', |
1167 'external', |
1168 ) or props not in (r'', r'none', r'normal'): |
1168 ) or props not in ('', 'none', 'normal'): |
1169 changes.append(path) |
1169 changes.append(path) |
1170 for path in changes: |
1170 for path in changes: |
1171 for ext in externals: |
1171 for ext in externals: |
1172 if path == ext or path.startswith(ext + pycompat.ossep): |
1172 if path == ext or path.startswith(ext + pycompat.ossep): |
1173 return True, True, bool(missing) |
1173 return True, True, bool(missing) |
1289 @annotatesubrepoerror |
1289 @annotatesubrepoerror |
1290 def files(self): |
1290 def files(self): |
1291 output = self._svncommand([b'list', b'--recursive', b'--xml'])[0] |
1291 output = self._svncommand([b'list', b'--recursive', b'--xml'])[0] |
1292 doc = xml.dom.minidom.parseString(output) |
1292 doc = xml.dom.minidom.parseString(output) |
1293 paths = [] |
1293 paths = [] |
1294 for e in doc.getElementsByTagName(r'entry'): |
1294 for e in doc.getElementsByTagName('entry'): |
1295 kind = pycompat.bytestr(e.getAttribute(r'kind')) |
1295 kind = pycompat.bytestr(e.getAttribute('kind')) |
1296 if kind != b'file': |
1296 if kind != b'file': |
1297 continue |
1297 continue |
1298 name = r''.join( |
1298 name = ''.join( |
1299 c.data |
1299 c.data |
1300 for c in e.getElementsByTagName(r'name')[0].childNodes |
1300 for c in e.getElementsByTagName('name')[0].childNodes |
1301 if c.nodeType == c.TEXT_NODE |
1301 if c.nodeType == c.TEXT_NODE |
1302 ) |
1302 ) |
1303 paths.append(name.encode('utf8')) |
1303 paths.append(name.encode('utf8')) |
1304 return paths |
1304 return paths |
1305 |
1305 |
1847 |
1847 |
1848 # Parse git's native archive command. |
1848 # Parse git's native archive command. |
1849 # This should be much faster than manually traversing the trees |
1849 # This should be much faster than manually traversing the trees |
1850 # and objects with many subprocess calls. |
1850 # and objects with many subprocess calls. |
1851 tarstream = self._gitcommand([b'archive', revision], stream=True) |
1851 tarstream = self._gitcommand([b'archive', revision], stream=True) |
1852 tar = tarfile.open(fileobj=tarstream, mode=r'r|') |
1852 tar = tarfile.open(fileobj=tarstream, mode='r|') |
1853 relpath = subrelpath(self) |
1853 relpath = subrelpath(self) |
1854 progress = self.ui.makeprogress( |
1854 progress = self.ui.makeprogress( |
1855 _(b'archiving (%s)') % relpath, unit=_(b'files') |
1855 _(b'archiving (%s)') % relpath, unit=_(b'files') |
1856 ) |
1856 ) |
1857 progress.update(0) |
1857 progress.update(0) |
1916 removed.append(f) |
1916 removed.append(f) |
1917 |
1917 |
1918 deleted, unknown, ignored, clean = [], [], [], [] |
1918 deleted, unknown, ignored, clean = [], [], [], [] |
1919 |
1919 |
1920 command = [b'status', b'--porcelain', b'-z'] |
1920 command = [b'status', b'--porcelain', b'-z'] |
1921 if opts.get(r'unknown'): |
1921 if opts.get('unknown'): |
1922 command += [b'--untracked-files=all'] |
1922 command += [b'--untracked-files=all'] |
1923 if opts.get(r'ignored'): |
1923 if opts.get('ignored'): |
1924 command += [b'--ignored'] |
1924 command += [b'--ignored'] |
1925 out = self._gitcommand(command) |
1925 out = self._gitcommand(command) |
1926 |
1926 |
1927 changedfiles = set() |
1927 changedfiles = set() |
1928 changedfiles.update(modified) |
1928 changedfiles.update(modified) |
1946 if st == b'??': |
1946 if st == b'??': |
1947 unknown.append(filename1) |
1947 unknown.append(filename1) |
1948 elif st == b'!!': |
1948 elif st == b'!!': |
1949 ignored.append(filename1) |
1949 ignored.append(filename1) |
1950 |
1950 |
1951 if opts.get(r'clean'): |
1951 if opts.get('clean'): |
1952 out = self._gitcommand([b'ls-files']) |
1952 out = self._gitcommand([b'ls-files']) |
1953 for f in out.split(b'\n'): |
1953 for f in out.split(b'\n'): |
1954 if not f in changedfiles: |
1954 if not f in changedfiles: |
1955 clean.append(f) |
1955 clean.append(f) |
1956 |
1956 |
1960 |
1960 |
1961 @annotatesubrepoerror |
1961 @annotatesubrepoerror |
1962 def diff(self, ui, diffopts, node2, match, prefix, **opts): |
1962 def diff(self, ui, diffopts, node2, match, prefix, **opts): |
1963 node1 = self._state[1] |
1963 node1 = self._state[1] |
1964 cmd = [b'diff', b'--no-renames'] |
1964 cmd = [b'diff', b'--no-renames'] |
1965 if opts[r'stat']: |
1965 if opts['stat']: |
1966 cmd.append(b'--stat') |
1966 cmd.append(b'--stat') |
1967 else: |
1967 else: |
1968 # for Git, this also implies '-p' |
1968 # for Git, this also implies '-p' |
1969 cmd.append(b'-U%d' % diffopts.context) |
1969 cmd.append(b'-U%d' % diffopts.context) |
1970 |
1970 |
2005 ui.write(output) |
2005 ui.write(output) |
2006 |
2006 |
2007 @annotatesubrepoerror |
2007 @annotatesubrepoerror |
2008 def revert(self, substate, *pats, **opts): |
2008 def revert(self, substate, *pats, **opts): |
2009 self.ui.status(_(b'reverting subrepo %s\n') % substate[0]) |
2009 self.ui.status(_(b'reverting subrepo %s\n') % substate[0]) |
2010 if not opts.get(r'no_backup'): |
2010 if not opts.get('no_backup'): |
2011 status = self.status(None) |
2011 status = self.status(None) |
2012 names = status.modified |
2012 names = status.modified |
2013 for name in names: |
2013 for name in names: |
2014 # backuppath() expects a path relative to the parent repo (the |
2014 # backuppath() expects a path relative to the parent repo (the |
2015 # repo that ui.origbackuppath is relative to) |
2015 # repo that ui.origbackuppath is relative to) |
2021 _(b'saving current version of %s as %s\n') |
2021 _(b'saving current version of %s as %s\n') |
2022 % (name, os.path.relpath(bakname)) |
2022 % (name, os.path.relpath(bakname)) |
2023 ) |
2023 ) |
2024 util.rename(self.wvfs.join(name), bakname) |
2024 util.rename(self.wvfs.join(name), bakname) |
2025 |
2025 |
2026 if not opts.get(r'dry_run'): |
2026 if not opts.get('dry_run'): |
2027 self.get(substate, overwrite=True) |
2027 self.get(substate, overwrite=True) |
2028 return [] |
2028 return [] |
2029 |
2029 |
2030 def shortid(self, revid): |
2030 def shortid(self, revid): |
2031 return revid[:7] |
2031 return revid[:7] |