hgext/imerge.py
changeset 5065 12930b97a729
parent 5064 420e1166a876
child 5117 d4fa6bafc43a
equal deleted inserted replaced
5064:420e1166a876 5065:12930b97a729
     5 imerge - interactive merge
     5 imerge - interactive merge
     6 '''
     6 '''
     7 
     7 
     8 from mercurial.i18n import _
     8 from mercurial.i18n import _
     9 from mercurial.node import *
     9 from mercurial.node import *
    10 from mercurial import commands, cmdutil, hg, merge, util
    10 from mercurial import commands, cmdutil, fancyopts, hg, merge, util
    11 import os, tarfile
    11 import os, tarfile
    12 
    12 
    13 class InvalidStateFileException(Exception): pass
    13 class InvalidStateFileException(Exception): pass
    14 
    14 
    15 class ImergeStateFile(object):
    15 class ImergeStateFile(object):
   132 
   132 
   133         dp = self.repo.dirstate.parents()
   133         dp = self.repo.dirstate.parents()
   134         p1, p2 = self.wctx.parents()
   134         p1, p2 = self.wctx.parents()
   135         if p1.node() != dp[0] or p2.node() != dp[1]:
   135         if p1.node() != dp[0] or p2.node() != dp[1]:
   136             raise util.Abort('imerge state does not match working directory')
   136             raise util.Abort('imerge state does not match working directory')
   137 
       
   138     def status(self):
       
   139         p1, p2 = self.wctx.parents()
       
   140         self.ui.write('merging %s and %s\n' % \
       
   141                       (short(p1.node()), short(p2.node())))
       
   142 
       
   143         if self.resolved:
       
   144             self.ui.write('resolved:\n')
       
   145             for fn in self.resolved:
       
   146                 self.ui.write('  %s\n' % fn)
       
   147         remaining = [f for f in self.conflicts if f not in self.resolved]
       
   148         if remaining:
       
   149             self.ui.write('remaining:\n')
       
   150             for fn in remaining:
       
   151                 (fd, fo) = self.conflicts[fn]
       
   152                 if fn == fo:
       
   153                     self.ui.write('  %s\n' % (fn,))
       
   154                 else:
       
   155                     self.ui.write('  %s (%s)\n' % (fn, fd))
       
   156         else:
       
   157             self.ui.write('all conflicts resolved\n')
       
   158 
   137 
   159     def next(self):
   138     def next(self):
   160         remaining = self.remaining()
   139         remaining = self.remaining()
   161         return remaining and remaining[0]
   140         return remaining and remaining[0]
   162 
   141 
   200     if m or a or r or d:
   179     if m or a or r or d:
   201         raise util.Abort('working directory has uncommitted changes')
   180         raise util.Abort('working directory has uncommitted changes')
   202 
   181 
   203     rc = im.unpickle(source)
   182     rc = im.unpickle(source)
   204     if not rc:
   183     if not rc:
   205         im.status()
   184         status(im)
   206     return rc
   185     return rc
   207 
   186 
   208 def merge_(im, filename=None):
   187 def merge_(im, filename=None):
   209     if not filename:
   188     if not filename:
   210         filename = im.next()
   189         filename = im.next()
   234     return im.resolve(files)
   213     return im.resolve(files)
   235 
   214 
   236 def save(im, dest):
   215 def save(im, dest):
   237     return im.pickle(dest)
   216     return im.pickle(dest)
   238 
   217 
   239 def status(im):
   218 def status(im, **opts):
   240     im.status()
   219     if not opts.get('resolved') and not opts.get('unresolved'):
       
   220         opts['resolved'] = True
       
   221         opts['unresolved'] = True
       
   222 
       
   223     if im.ui.verbose:
       
   224         p1, p2 = [short(p.node()) for p in im.wctx.parents()]
       
   225         im.ui.note(_('merging %s and %s\n') % (p1, p2))
       
   226 
       
   227     conflicts = im.conflicts.keys()
       
   228     conflicts.sort()
       
   229     remaining = dict.fromkeys(im.remaining())
       
   230     st = []
       
   231     for fn in conflicts:
       
   232         if opts.get('no_status'):
       
   233             mode = ''
       
   234         elif fn in remaining:
       
   235             mode = 'U '
       
   236         else:
       
   237             mode = 'R '
       
   238         if ((opts.get('resolved') and fn not in remaining)
       
   239             or (opts.get('unresolved') and fn in remaining)):
       
   240             st.append((mode, fn))
       
   241     st.sort()
       
   242     for (mode, fn) in st:
       
   243         if im.ui.verbose:
       
   244             fo, fd = im.conflicts[fn]
       
   245             if fd != fn:
       
   246                 fn = '%s (%s)' % (fn, fd)
       
   247         im.ui.write('%s%s\n' % (mode, fn))
       
   248     if opts.get('unresolved') and not remaining:
       
   249         im.ui.write(_('all conflicts resolved\n'))
       
   250 
   241     return 0
   251     return 0
   242 
   252 
   243 def unresolve(im, *files):
   253 def unresolve(im, *files):
   244     if not files:
   254     if not files:
   245         raise util.Abort('unresolve requires at least one filename')
   255         raise util.Abort('unresolve requires at least one filename')
   246     return im.unresolve(files)
   256     return im.unresolve(files)
   247 
   257 
   248 subcmdtable = {
   258 subcmdtable = {
   249     'load': load,
   259     'load': (load, []),
   250     'merge': merge_,
   260     'merge': (merge_, []),
   251     'next': next,
   261     'next': (next, []),
   252     'resolve': resolve,
   262     'resolve': (resolve, []),
   253     'save': save,
   263     'save': (save, []),
   254     'status': status,
   264     'status': (status,
   255     'unresolve': unresolve
   265                [('n', 'no-status', None, _('hide status prefix')),
       
   266                 ('', 'resolved', None, _('only show resolved conflicts')),
       
   267                 ('', 'unresolved', None, _('only show unresolved conflicts'))]),
       
   268     'unresolve': (unresolve, [])
   256 }
   269 }
   257 
   270 
   258 def dispatch(im, args, opts):
   271 def dispatch(im, args, opts):
   259     def complete(s, choices):
   272     def complete(s, choices):
   260         candidates = []
   273         candidates = []
   261         for choice in choices:
   274         for choice in choices:
   262             if choice.startswith(s):
   275             if choice.startswith(s):
   263                 candidates.append(choice)
   276                 candidates.append(choice)
   264         return candidates
   277         return candidates
   265 
   278 
   266     c, args = args[0], args[1:]
   279     c, args = args[0], list(args[1:])
   267     cmd = complete(c, subcmdtable.keys())
   280     cmd = complete(c, subcmdtable.keys())
   268     if not cmd:
   281     if not cmd:
   269         raise cmdutil.UnknownCommand('imerge ' + c)
   282         raise cmdutil.UnknownCommand('imerge ' + c)
   270     if len(cmd) > 1:
   283     if len(cmd) > 1:
   271         cmd.sort()
   284         cmd.sort()
   272         raise cmdutil.AmbiguousCommand('imerge ' + c, cmd)
   285         raise cmdutil.AmbiguousCommand('imerge ' + c, cmd)
   273     cmd = cmd[0]
   286     cmd = cmd[0]
   274 
   287 
   275     func = subcmdtable[cmd]
   288     func, optlist = subcmdtable[cmd]
       
   289     opts = {}
   276     try:
   290     try:
   277         return func(im, *args)
   291         args = fancyopts.fancyopts(args, optlist, opts)
       
   292         return func(im, *args, **opts)
       
   293     except fancyopts.getopt.GetoptError, inst:
       
   294         raise cmdutil.ParseError('imerge', '%s: %s' % (cmd, inst))
   278     except TypeError:
   295     except TypeError:
   279         raise cmdutil.ParseError('imerge', '%s: invalid arguments' % cmd)
   296         raise cmdutil.ParseError('imerge', _('%s: invalid arguments') % cmd)
   280 
   297 
   281 def imerge(ui, repo, *args, **opts):
   298 def imerge(ui, repo, *args, **opts):
   282     '''interactive merge
   299     '''interactive merge
   283 
   300 
   284     imerge lets you split a merge into pieces. When you start a merge
   301     imerge lets you split a merge into pieces. When you start a merge