mercurial/changegroup.py
changeset 49610 35d4c2124073
parent 49609 9cac281eb9c0
child 49766 152d9c011bcd
equal deleted inserted replaced
49609:9cac281eb9c0 49610:35d4c2124073
   103                 vfs.unlink(cleanup)
   103                 vfs.unlink(cleanup)
   104             else:
   104             else:
   105                 os.unlink(cleanup)
   105                 os.unlink(cleanup)
   106 
   106 
   107 
   107 
       
   108 def _dbg_ubdl_line(
       
   109     ui,
       
   110     indent,
       
   111     key,
       
   112     base_value=None,
       
   113     percentage_base=None,
       
   114     percentage_key=None,
       
   115 ):
       
   116     """Print one line of debug_unbundle_debug_info"""
       
   117     line = b"DEBUG-UNBUNDLING: "
       
   118     line += b' ' * (2 * indent)
       
   119     key += b":"
       
   120     padding = b''
       
   121     if base_value is not None:
       
   122         assert len(key) + 1 + (2 * indent) <= _KEY_PART_WIDTH
       
   123         line += key.ljust(_KEY_PART_WIDTH - (2 * indent))
       
   124         if isinstance(base_value, float):
       
   125             line += b"%14.3f seconds" % base_value
       
   126         else:
       
   127             line += b"%10d" % base_value
       
   128             padding = b'            '
       
   129     else:
       
   130         line += key
       
   131 
       
   132     if percentage_base is not None:
       
   133         line += padding
       
   134         padding = b''
       
   135         assert base_value is not None
       
   136         percentage = base_value * 100 // percentage_base
       
   137         if percentage_key is not None:
       
   138             line += b" (%3d%% of %s)" % (
       
   139                 percentage,
       
   140                 percentage_key,
       
   141             )
       
   142         else:
       
   143             line += b" (%3d%%)" % percentage
       
   144 
       
   145     line += b'\n'
       
   146     ui.write_err(line)
       
   147 
       
   148 
       
   149 def _sumf(items):
       
   150     # python < 3.8 does not support a `start=0.0` argument to sum
       
   151     # So we have to cheat a bit until we drop support for those version
       
   152     if not items:
       
   153         return 0.0
       
   154     return sum(items)
       
   155 
       
   156 
       
   157 def display_unbundle_debug_info(ui, debug_info):
       
   158     """display an unbundling report from debug information"""
       
   159     cl_info = []
       
   160     mn_info = []
       
   161     fl_info = []
       
   162     _dispatch = [
       
   163         (b'CHANGELOG:', cl_info),
       
   164         (b'MANIFESTLOG:', mn_info),
       
   165         (b'FILELOG:', fl_info),
       
   166     ]
       
   167     for e in debug_info:
       
   168         for prefix, info in _dispatch:
       
   169             if e["target-revlog"].startswith(prefix):
       
   170                 info.append(e)
       
   171                 break
       
   172         else:
       
   173             assert False, 'unreachable'
       
   174     each_info = [
       
   175         (b'changelog', cl_info),
       
   176         (b'manifests', mn_info),
       
   177         (b'files', fl_info),
       
   178     ]
       
   179 
       
   180     # General Revision Countss
       
   181     _dbg_ubdl_line(ui, 0, b'revisions', len(debug_info))
       
   182     for key, info in each_info:
       
   183         if not info:
       
   184             continue
       
   185         _dbg_ubdl_line(ui, 1, key, len(info), len(debug_info))
       
   186 
       
   187     # General Time spent
       
   188     all_durations = [e['duration'] for e in debug_info]
       
   189     all_durations.sort()
       
   190     total_duration = _sumf(all_durations)
       
   191     _dbg_ubdl_line(ui, 0, b'total-time', total_duration)
       
   192 
       
   193     for key, info in each_info:
       
   194         if not info:
       
   195             continue
       
   196         durations = [e['duration'] for e in info]
       
   197         durations.sort()
       
   198         _dbg_ubdl_line(ui, 1, key, _sumf(durations), total_duration)
       
   199 
       
   200     # Count and cache reuse per delta types
       
   201     each_types = {}
       
   202     for key, info in each_info:
       
   203         each_types[key] = types = {
       
   204             b'full': 0,
       
   205             b'full-cached': 0,
       
   206             b'snapshot': 0,
       
   207             b'snapshot-cached': 0,
       
   208             b'delta': 0,
       
   209             b'delta-cached': 0,
       
   210             b'unknown': 0,
       
   211             b'unknown-cached': 0,
       
   212         }
       
   213         for e in info:
       
   214             types[e['type']] += 1
       
   215             if e['using-cached-base']:
       
   216                 types[e['type'] + b'-cached'] += 1
       
   217 
       
   218     EXPECTED_TYPES = (b'full', b'snapshot', b'delta', b'unknown')
       
   219     if debug_info:
       
   220         _dbg_ubdl_line(ui, 0, b'type-count')
       
   221     for key, info in each_info:
       
   222         if info:
       
   223             _dbg_ubdl_line(ui, 1, key)
       
   224         t = each_types[key]
       
   225         for tn in EXPECTED_TYPES:
       
   226             if t[tn]:
       
   227                 tc = tn + b'-cached'
       
   228                 _dbg_ubdl_line(ui, 2, tn, t[tn])
       
   229                 _dbg_ubdl_line(ui, 3, b'cached', t[tc], t[tn])
       
   230 
       
   231     # time perf delta types and reuse
       
   232     each_type_time = {}
       
   233     for key, info in each_info:
       
   234         each_type_time[key] = t = {
       
   235             b'full': [],
       
   236             b'full-cached': [],
       
   237             b'snapshot': [],
       
   238             b'snapshot-cached': [],
       
   239             b'delta': [],
       
   240             b'delta-cached': [],
       
   241             b'unknown': [],
       
   242             b'unknown-cached': [],
       
   243         }
       
   244         for e in info:
       
   245             t[e['type']].append(e['duration'])
       
   246             if e['using-cached-base']:
       
   247                 t[e['type'] + b'-cached'].append(e['duration'])
       
   248         for t_key, value in list(t.items()):
       
   249             value.sort()
       
   250             t[t_key] = _sumf(value)
       
   251 
       
   252     if debug_info:
       
   253         _dbg_ubdl_line(ui, 0, b'type-time')
       
   254     for key, info in each_info:
       
   255         if info:
       
   256             _dbg_ubdl_line(ui, 1, key)
       
   257         t = each_type_time[key]
       
   258         td = total_duration  # to same space on next lines
       
   259         for tn in EXPECTED_TYPES:
       
   260             if t[tn]:
       
   261                 tc = tn + b'-cached'
       
   262                 _dbg_ubdl_line(ui, 2, tn, t[tn], td, b"total")
       
   263                 _dbg_ubdl_line(ui, 3, b'cached', t[tc], td, b"total")
       
   264 
       
   265 
   108 class cg1unpacker:
   266 class cg1unpacker:
   109     """Unpacker for cg1 changegroup streams.
   267     """Unpacker for cg1 changegroup streams.
   110 
   268 
   111     A changegroup unpacker handles the framing of the revision data in
   269     A changegroup unpacker handles the framing of the revision data in
   112     the wire format. Most consumers will want to use the apply()
   270     the wire format. Most consumers will want to use the apply()
   252                     next = pos + 2 ** 20
   410                     next = pos + 2 ** 20
   253                     yield chunk[pos:next]
   411                     yield chunk[pos:next]
   254                     pos = next
   412                     pos = next
   255             yield closechunk()
   413             yield closechunk()
   256 
   414 
   257     def _unpackmanifests(self, repo, revmap, trp, prog, addrevisioncb=None):
   415     def _unpackmanifests(
       
   416         self,
       
   417         repo,
       
   418         revmap,
       
   419         trp,
       
   420         prog,
       
   421         addrevisioncb=None,
       
   422         debug_info=None,
       
   423     ):
   258         self.callback = prog.increment
   424         self.callback = prog.increment
   259         # no need to check for empty manifest group here:
   425         # no need to check for empty manifest group here:
   260         # if the result of the merge of 1 and 2 is the same in 3 and 4,
   426         # if the result of the merge of 1 and 2 is the same in 3 and 4,
   261         # no new manifest will be created and the manifest group will
   427         # no new manifest will be created and the manifest group will
   262         # be empty during the pull
   428         # be empty during the pull
   263         self.manifestheader()
   429         self.manifestheader()
   264         deltas = self.deltaiter()
   430         deltas = self.deltaiter()
   265         storage = repo.manifestlog.getstorage(b'')
   431         storage = repo.manifestlog.getstorage(b'')
   266         storage.addgroup(deltas, revmap, trp, addrevisioncb=addrevisioncb)
   432         storage.addgroup(
       
   433             deltas,
       
   434             revmap,
       
   435             trp,
       
   436             addrevisioncb=addrevisioncb,
       
   437             debug_info=debug_info,
       
   438         )
   267         prog.complete()
   439         prog.complete()
   268         self.callback = None
   440         self.callback = None
   269 
   441 
   270     def apply(
   442     def apply(
   271         self,
   443         self,
   289 
   461 
   290         `sidedata_categories` is an optional set of the remote's sidedata wanted
   462         `sidedata_categories` is an optional set of the remote's sidedata wanted
   291         categories.
   463         categories.
   292         """
   464         """
   293         repo = repo.unfiltered()
   465         repo = repo.unfiltered()
       
   466 
       
   467         debug_info = None
       
   468         if repo.ui.configbool(b'debug', b'unbundling-stats'):
       
   469             debug_info = []
   294 
   470 
   295         # Only useful if we're adding sidedata categories. If both peers have
   471         # Only useful if we're adding sidedata categories. If both peers have
   296         # the same categories, then we simply don't do anything.
   472         # the same categories, then we simply don't do anything.
   297         adding_sidedata = (
   473         adding_sidedata = (
   298             (
   474             (
   364                 csmap,
   540                 csmap,
   365                 trp,
   541                 trp,
   366                 alwayscache=True,
   542                 alwayscache=True,
   367                 addrevisioncb=onchangelog,
   543                 addrevisioncb=onchangelog,
   368                 duplicaterevisioncb=ondupchangelog,
   544                 duplicaterevisioncb=ondupchangelog,
       
   545                 debug_info=debug_info,
   369             ):
   546             ):
   370                 repo.ui.develwarn(
   547                 repo.ui.develwarn(
   371                     b'applied empty changelog from changegroup',
   548                     b'applied empty changelog from changegroup',
   372                     config=b'warn-empty-changegroup',
   549                     config=b'warn-empty-changegroup',
   373                 )
   550                 )
   411                 repo,
   588                 repo,
   412                 revmap,
   589                 revmap,
   413                 trp,
   590                 trp,
   414                 progress,
   591                 progress,
   415                 addrevisioncb=on_manifest_rev,
   592                 addrevisioncb=on_manifest_rev,
       
   593                 debug_info=debug_info,
   416             )
   594             )
   417 
   595 
   418             needfiles = {}
   596             needfiles = {}
   419             if repo.ui.configbool(b'server', b'validate'):
   597             if repo.ui.configbool(b'server', b'validate'):
   420                 cl = repo.changelog
   598                 cl = repo.changelog
   447                 revmap,
   625                 revmap,
   448                 trp,
   626                 trp,
   449                 efiles,
   627                 efiles,
   450                 needfiles,
   628                 needfiles,
   451                 addrevisioncb=on_filelog_rev,
   629                 addrevisioncb=on_filelog_rev,
       
   630                 debug_info=debug_info,
   452             )
   631             )
   453 
   632 
   454             if sidedata_helpers:
   633             if sidedata_helpers:
   455                 if revlog_constants.KIND_CHANGELOG in sidedata_helpers[1]:
   634                 if revlog_constants.KIND_CHANGELOG in sidedata_helpers[1]:
   456                     cl.rewrite_sidedata(
   635                     cl.rewrite_sidedata(
   565 
   744 
   566                 tr.addpostclose(
   745                 tr.addpostclose(
   567                     b'changegroup-runhooks-%020i' % clstart,
   746                     b'changegroup-runhooks-%020i' % clstart,
   568                     lambda tr: repo._afterlock(runhooks),
   747                     lambda tr: repo._afterlock(runhooks),
   569                 )
   748                 )
       
   749             if debug_info is not None:
       
   750                 display_unbundle_debug_info(repo.ui, debug_info)
   570         finally:
   751         finally:
   571             repo.ui.flush()
   752             repo.ui.flush()
   572         # never return 0 here:
   753         # never return 0 here:
   573         if deltaheads < 0:
   754         if deltaheads < 0:
   574             ret = deltaheads - 1
   755             ret = deltaheads - 1
   624     def _deltaheader(self, headertuple, prevnode):
   805     def _deltaheader(self, headertuple, prevnode):
   625         node, p1, p2, deltabase, cs, flags = headertuple
   806         node, p1, p2, deltabase, cs, flags = headertuple
   626         protocol_flags = 0
   807         protocol_flags = 0
   627         return node, p1, p2, deltabase, cs, flags, protocol_flags
   808         return node, p1, p2, deltabase, cs, flags, protocol_flags
   628 
   809 
   629     def _unpackmanifests(self, repo, revmap, trp, prog, addrevisioncb=None):
   810     def _unpackmanifests(
       
   811         self,
       
   812         repo,
       
   813         revmap,
       
   814         trp,
       
   815         prog,
       
   816         addrevisioncb=None,
       
   817         debug_info=None,
       
   818     ):
   630         super(cg3unpacker, self)._unpackmanifests(
   819         super(cg3unpacker, self)._unpackmanifests(
   631             repo, revmap, trp, prog, addrevisioncb=addrevisioncb
   820             repo,
       
   821             revmap,
       
   822             trp,
       
   823             prog,
       
   824             addrevisioncb=addrevisioncb,
       
   825             debug_info=debug_info,
   632         )
   826         )
   633         for chunkdata in iter(self.filelogheader, {}):
   827         for chunkdata in iter(self.filelogheader, {}):
   634             # If we get here, there are directory manifests in the changegroup
   828             # If we get here, there are directory manifests in the changegroup
   635             d = chunkdata[b"filename"]
   829             d = chunkdata[b"filename"]
   636             repo.ui.debug(b"adding %s revisions\n" % d)
   830             repo.ui.debug(b"adding %s revisions\n" % d)
   637             deltas = self.deltaiter()
   831             deltas = self.deltaiter()
   638             if not repo.manifestlog.getstorage(d).addgroup(
   832             if not repo.manifestlog.getstorage(d).addgroup(
   639                 deltas, revmap, trp, addrevisioncb=addrevisioncb
   833                 deltas,
       
   834                 revmap,
       
   835                 trp,
       
   836                 addrevisioncb=addrevisioncb,
       
   837                 debug_info=debug_info,
   640             ):
   838             ):
   641                 raise error.Abort(_(b"received dir revlog group is empty"))
   839                 raise error.Abort(_(b"received dir revlog group is empty"))
   642 
   840 
   643 
   841 
   644 class cg4unpacker(cg3unpacker):
   842 class cg4unpacker(cg3unpacker):
  2171     revmap,
  2369     revmap,
  2172     trp,
  2370     trp,
  2173     expectedfiles,
  2371     expectedfiles,
  2174     needfiles,
  2372     needfiles,
  2175     addrevisioncb=None,
  2373     addrevisioncb=None,
       
  2374     debug_info=None,
  2176 ):
  2375 ):
  2177     revisions = 0
  2376     revisions = 0
  2178     files = 0
  2377     files = 0
  2179     progress = repo.ui.makeprogress(
  2378     progress = repo.ui.makeprogress(
  2180         _(b'files'), unit=_(b'files'), total=expectedfiles
  2379         _(b'files'), unit=_(b'files'), total=expectedfiles
  2191             added = fl.addgroup(
  2390             added = fl.addgroup(
  2192                 deltas,
  2391                 deltas,
  2193                 revmap,
  2392                 revmap,
  2194                 trp,
  2393                 trp,
  2195                 addrevisioncb=addrevisioncb,
  2394                 addrevisioncb=addrevisioncb,
       
  2395                 debug_info=debug_info,
  2196             )
  2396             )
  2197             if not added:
  2397             if not added:
  2198                 raise error.Abort(_(b"received file revlog group is empty"))
  2398                 raise error.Abort(_(b"received file revlog group is empty"))
  2199         except error.CensoredBaseError as e:
  2399         except error.CensoredBaseError as e:
  2200             raise error.Abort(_(b"received delta base is censored: %s") % e)
  2400             raise error.Abort(_(b"received delta base is censored: %s") % e)