contrib/simplemerge
changeset 4360 d5c3a70f8422
parent 4359 2e3c54fb79a3
child 4406 1ef4445c6506
equal deleted inserted replaced
4359:2e3c54fb79a3 4360:d5c3a70f8422
    17 
    17 
    18 
    18 
    19 # mbp: "you know that thing where cvs gives you conflict markers?"
    19 # mbp: "you know that thing where cvs gives you conflict markers?"
    20 # s: "i hate that."
    20 # s: "i hate that."
    21 
    21 
    22 
    22 from mercurial import demandimport
    23 from mercurial import util, mdiff
    23 demandimport.enable()
       
    24 
       
    25 from mercurial import util, mdiff, fancyopts
    24 from mercurial.i18n import _
    26 from mercurial.i18n import _
    25 
    27 
    26 
    28 
    27 class CantReprocessAndShowBase(Exception):
    29 class CantReprocessAndShowBase(Exception):
    28     pass
    30     pass
    95                     end_marker='>>>>>>>',
    97                     end_marker='>>>>>>>',
    96                     base_marker=None,
    98                     base_marker=None,
    97                     reprocess=False):
    99                     reprocess=False):
    98         """Return merge in cvs-like form.
   100         """Return merge in cvs-like form.
    99         """
   101         """
       
   102         self.conflicts = False
   100         newline = '\n'
   103         newline = '\n'
   101         if len(self.a) > 0:
   104         if len(self.a) > 0:
   102             if self.a[0].endswith('\r\n'):
   105             if self.a[0].endswith('\r\n'):
   103                 newline = '\r\n'
   106                 newline = '\r\n'
   104             elif self.a[0].endswith('\r'):
   107             elif self.a[0].endswith('\r'):
   124                     yield self.a[i]
   127                     yield self.a[i]
   125             elif what == 'b':
   128             elif what == 'b':
   126                 for i in range(t[1], t[2]):
   129                 for i in range(t[1], t[2]):
   127                     yield self.b[i]
   130                     yield self.b[i]
   128             elif what == 'conflict':
   131             elif what == 'conflict':
       
   132                 self.conflicts = True
   129                 yield start_marker + newline
   133                 yield start_marker + newline
   130                 for i in range(t[3], t[4]):
   134                 for i in range(t[3], t[4]):
   131                     yield self.a[i]
   135                     yield self.a[i]
   132                 if base_marker is not None:
   136                 if base_marker is not None:
   133                     yield base_marker + newline
   137                     yield base_marker + newline
   437         if util.binary(basetext) or util.binary(atext) or util.binary(btext):
   441         if util.binary(basetext) or util.binary(atext) or util.binary(btext):
   438             raise util.Abort(_("don't know how to merge binary files"))
   442             raise util.Abort(_("don't know how to merge binary files"))
   439         Merge3Text.__init__(self, basetext, atext, btext, base, a, b)
   443         Merge3Text.__init__(self, basetext, atext, btext, base, a, b)
   440 
   444 
   441 
   445 
       
   446 def simplemerge(local, base, other, **opts):
       
   447     def readfile(filename):
       
   448         f = open(filename, "rb")
       
   449         text = f.read()
       
   450         f.close()
       
   451         if util.binary(text):
       
   452             msg = _("%s looks like a binary file.") % filename
       
   453             if not opts.get('text'):
       
   454                 raise util.Abort(msg)
       
   455             elif not opts.get('quiet'):
       
   456                 sys.stderr.write(_('warning: %s\n') % msg)
       
   457         return text
       
   458 
       
   459     name_a = local
       
   460     name_b = other
       
   461     labels = opts.get('label', [])
       
   462     if labels:
       
   463         name_a = labels.pop(0)
       
   464     if labels:
       
   465         name_b = labels.pop(0)
       
   466     if labels:
       
   467         raise util.Abort(_("can only specify two labels."))
       
   468 
       
   469     localtext = readfile(local)
       
   470     basetext = readfile(base)
       
   471     othertext = readfile(other)
       
   472 
       
   473     orig = local
       
   474     local = os.path.realpath(local)
       
   475     if not opts.get('print'):
       
   476         opener = util.opener(os.path.dirname(local))
       
   477         out = opener(os.path.basename(local), "w", atomictemp=True)
       
   478     else:
       
   479         out = sys.stdout
       
   480 
       
   481     reprocess = not opts.get('no_minimal')
       
   482 
       
   483     m3 = Merge3Text(basetext, localtext, othertext)
       
   484     for line in m3.merge_lines(name_a=name_a, name_b=name_b,
       
   485                                reprocess=reprocess):
       
   486         out.write(line)
       
   487 
       
   488     if not opts.get('print'):
       
   489         out.rename()
       
   490 
       
   491     if m3.conflicts:
       
   492         if not opts.get('quiet'):
       
   493             sys.stdout.flush()
       
   494             sys.stderr.write(_("warning: conflicts during merge.\n"))
       
   495         return 1
       
   496 
       
   497 options = [('L', 'label', [], _('labels to use on conflict markers')),
       
   498            ('a', 'text', None, _('treat all files as text')),
       
   499            ('p', 'print', None,
       
   500             _('print results instead of overwriting LOCAL')),
       
   501            ('', 'no-minimal', None,
       
   502             _('do not try to minimize conflict regions')),
       
   503            ('h', 'help', None, _('display help and exit')),
       
   504            ('q', 'quiet', None, _('suppress output'))]
       
   505 
       
   506 usage = _('''simplemerge [OPTS] LOCAL BASE OTHER
       
   507 
       
   508     Simple three-way file merge utility with a minimal feature set.
       
   509     
       
   510     Apply to LOCAL the changes necessary to go from BASE to OTHER.
       
   511     
       
   512     By default, LOCAL is overwritten with the results of this operation.
       
   513 ''')
       
   514 
       
   515 def showhelp():
       
   516     sys.stdout.write(usage)
       
   517     sys.stdout.write('\noptions:\n')
       
   518 
       
   519     out_opts = []
       
   520     for shortopt, longopt, default, desc in options:
       
   521         out_opts.append(('%2s%s' % (shortopt and '-%s' % shortopt,
       
   522                                     longopt and ' --%s' % longopt),
       
   523                          '%s' % desc))
       
   524     opts_len = max([len(opt[0]) for opt in out_opts])
       
   525     for first, second in out_opts:
       
   526         sys.stdout.write(' %-*s  %s\n' % (opts_len, first, second))
       
   527 
       
   528 class ParseError(Exception):
       
   529     """Exception raised on errors in parsing the command line."""
       
   530 
   442 def main(argv):
   531 def main(argv):
   443     # as for diff3 and meld the syntax is "MINE BASE OTHER"
   532     try:
   444     a = file(argv[1], 'rt').readlines()
   533         opts = {}
   445     base = file(argv[2], 'rt').readlines()
   534         try:
   446     b = file(argv[3], 'rt').readlines()
   535             args = fancyopts.fancyopts(argv[1:], options, opts)
   447 
   536         except fancyopts.getopt.GetoptError, e:
   448     m3 = Merge3(base, a, b)
   537             raise ParseError(e)
   449 
   538         if opts['help']:
   450     #for sr in m3.find_sync_regions():
   539             showhelp()
   451     #    print sr
   540             return 0
   452 
   541         if len(args) != 3:
   453     # sys.stdout.writelines(m3.merge_lines(name_a=argv[1], name_b=argv[3]))
   542                 raise ParseError(_('wrong number of arguments'))
   454     sys.stdout.writelines(m3.merge_annotated())
   543         return simplemerge(*args, **opts)
   455 
   544     except ParseError, e:
       
   545         sys.stdout.write("%s: %s\n" % (sys.argv[0], e))
       
   546         showhelp()
       
   547         return 1
       
   548     except util.Abort, e:
       
   549         sys.stderr.write("abort: %s\n" % e)
       
   550         return 255
       
   551     except KeyboardInterrupt:
       
   552         return 255
   456 
   553 
   457 if __name__ == '__main__':
   554 if __name__ == '__main__':
   458     import sys
   555     import sys
       
   556     import os
   459     sys.exit(main(sys.argv))
   557     sys.exit(main(sys.argv))