Commits

Audrius Kažukauskas committed c2ee836

Update hgshelve.py to 220e4aa changeset.

Comments (0)

Files changed (1)

 '''interactive change selection to set aside that may be restored later'''
 
 from mercurial.i18n import _
-from mercurial import cmdutil, commands, cmdutil, hg, mdiff, patch, revlog
+from mercurial import cmdutil, commands, hg, mdiff, patch, scmutil
 from mercurial import util, fancyopts, extensions
-import copy, cStringIO, errno, operator, os, re, shutil, tempfile
+import copy, cStringIO, errno, operator, os, re, shutil, tempfile, sys
 
 lines_re = re.compile(r'@@ -(\d+),(\d+) \+(\d+),(\d+) @@\s*(.*)')
 
         self.added, self.removed = countchanges(self.hunk)
 
     def __cmp__(self, rhs):
-        # since the hunk().toline needs to be adjusted when hunks are
+        # since the hunk().fromline needs to be adjusted when hunks are
         # removed/added, we can't take it into account when we cmp
-        attrs = ['header', 'fromline', 'proc', 'hunk', 'added', 'removed']
+        attrs = ['header', 'toline', 'proc', 'hunk', 'added', 'removed']
         for attr in attrs:
             selfattr = getattr(self, attr, None)
             rhsattr = getattr(rhs, attr, None)
             else:
                 consumed.append(chunks.pop())
         return consumed
-    
+
     resp_all = [None]
 
-    """ If we're not to prompt (i.e. they specified the --all flag) 
+    """ If we're not to prompt (i.e. they specified the --all flag)
         we pre-emptively set the 'all' flag """
     if shouldprompt == False:
         resp_all = ['y']
-    
+
     resp_file = [None]
     applied = {}
     def prompt(query):
                        _(' and ').join(map(repr, chunk.files())))
             else:
                 r = 'y'
-            
+
             if r == 'y':
                 applied[chunk.filename()] = [chunk]
                 if chunk.allhunks():
             if r == 'y':
                 if fixoffset:
                     chunk = copy.copy(chunk)
-                    chunk.toline += fixoffset
+                    chunk.fromline += fixoffset
                 applied[chunk.filename()].append(chunk)
             else:
-                fixoffset += chunk.removed - chunk.added
+                fixoffset += chunk.added - chunk.removed
     return reduce(operator.add, [h for h in applied.itervalues()
                                  if h[0].special() or len(h) > 1], [])
 
 
     backups = {}
     for f in files:
-        fd, tmpname = tempfile.mkstemp(prefix=f.replace('/', '_')+'.',
-                                       dir=dir)
-        os.close(fd)
-        ui.debug('backup %r as %r\n' % (f, tmpname))
-        util.copyfile(repo.wjoin(f), tmpname)
-        backups[f] = tmpname
+        if os.path.isfile(repo.wjoin(f)):
+            fd, tmpname = tempfile.mkstemp(prefix=f.replace('/', '_')+'.',
+                                           dir=dir)
+            os.close(fd)
+            ui.debug('backup %r as %r\n' % (f, tmpname))
+            util.copyfile(repo.wjoin(f), tmpname)
+            backups[f] = tmpname
 
     return backups
 
             shelfpath = 'shelve'
         else:
             shelfpath = "shelves/default"
-    
+
     return shelfpath
-    
+
 def shelve(ui, repo, *pats, **opts):
     '''interactively select changes to set aside
 
         return
 
     forced = opts['force'] or opts['append']
-    
+
     # Shelf name and path
     shelfname = opts.get('name')
     shelfpath = getshelfpath(repo, shelfname)
-    
+
     if os.path.exists(repo.join(shelfpath)) and not forced:
         raise util.Abort(_('shelve data already exists'))
-            
+
     def shelvefunc(ui, repo, message, match, opts):
+        parents = repo.dirstate.parents()
         changes = repo.status(match=match)[:5]
         modified, added, removed = changes[:3]
         files = modified + added + removed
         diffopts = mdiff.diffopts(git=True, nodates=True)
-        patch_diff = ''.join(patch.diff(repo, repo.dirstate.parents()[0],
+        patch_diff = ''.join(patch.diff(repo, parents[0],
                            match=match, changes=changes, opts=diffopts))
-        
+
         fp = cStringIO.StringIO(patch_diff)
         ac = parsepatch(fp)
         fp.close()
-        
+
         chunks = filterpatch(ui, ac, not opts['all'])
         rc = refilterpatch(ac, chunks)
 
+        # set of files to be processed
         contenders = {}
         for h in chunks:
             try: contenders.update(dict.fromkeys(h.files()))
             except AttributeError: pass
 
-        newfiles = [f for f in files if f in contenders]
+        # exclude sources of copies that are otherwise untouched
+        newfiles = set(f for f in files if f in contenders)
 
         if not newfiles:
             ui.status(_('no changes to shelve\n'))
             return 0
 
-        modified = dict.fromkeys(changes[0])
-
         backupdir = repo.join('shelve-backups')
 
         try:
-            bkfiles = [f for f in newfiles if f in modified]
-            backups = makebackup(ui, repo, backupdir, bkfiles)
-            
+            backups = makebackup(ui, repo, backupdir, newfiles)
+
             # patch to shelve
             sp = cStringIO.StringIO()
             for c in chunks:
-                if c.filename() in backups:
-                    c.write(sp)
-            doshelve = sp.tell()
-            sp.seek(0)
+                c.write(sp)
 
             # patch to apply to shelved files
             fp = cStringIO.StringIO()
             for c in rc:
-                if c.filename() in backups:
+                # skip files not selected for shelving
+                if c.filename() in newfiles:
                     c.write(fp)
             dopatch = fp.tell()
             fp.seek(0)
 
             try:
                 # 3a. apply filtered patch to clean repo (clean)
-                if backups:
-                    hg.revert(repo, repo.dirstate.parents()[0], backups.has_key)
+                opts['no_backup'] = True
+                cmdutil.revert(
+                    ui, repo, repo['.'], parents,
+                    *[
+                        os.path.join(repo.root, f)
+                        for f in newfiles
+                    ], **opts
+                )
+                for f in added:
+                    if f in newfiles:
+                        util.unlinkpath(repo.wjoin(f))
 
                 # 3b. apply filtered patch to clean repo (apply)
                 if dopatch:
                 del fp
 
                 # 3c. apply filtered patch to clean repo (shelve)
-                if doshelve:
-                    ui.debug("saving patch to shelve\n")
-                    if opts['append']:
-                        f = repo.opener(shelfpath, "a")
-                    else:
-                        f = repo.opener(shelfpath, "w")
-                    f.write(sp.getvalue())
-                    del f
-                del sp
+                ui.debug("saving patch to shelve\n")
+                if opts['append']:
+                    sp.write(repo.opener(shelfpath).read())
+                sp.seek(0)
+                f = repo.opener(shelfpath, "w")
+                f.write(sp.getvalue())
+                del f, sp
             except:
+                ui.warn("shelving failed: %s\n" % sys.exc_info()[1])
                 try:
+                    # re-schedule remove
+                    matchremoved = scmutil.matchfiles(repo, removed)
+                    cmdutil.forget(ui, repo, matchremoved, "", True)
+                    for f in removed:
+                        if f in newfiles and os.path.isfile(f):
+                            os.unlink(f)
+                    # copy back backups
                     for realname, tmpname in backups.iteritems():
                         ui.debug('restoring %r to %r\n' % (tmpname, realname))
                         util.copyfile(tmpname, repo.wjoin(realname))
+                    # re-schedule add
+                    matchadded = scmutil.matchfiles(repo, added)
+                    cmdutil.add(ui, repo, matchadded, False, False, "", True)
+
                     ui.debug('removing shelve file\n')
-                    os.unlink(repo.join(shelfpath))
-                except OSError:
+                    if os.path.isfile(repo.wjoin(shelfpath)):
+                        os.unlink(repo.join(shelfpath))
+                except OSError, err:
+                    ui.warn("restoring backup failed: %s\n" % err)
                     pass
 
             return 0
                     ui.debug('removing backup for %r : %r\n' % (realname, tmpname))
                     os.unlink(tmpname)
                 os.rmdir(backupdir)
-            except OSError:
-                pass
+            except OSError, err:
+                ui.warn("removing backup failed: %s\n" % err)
     fancyopts.fancyopts([], commands.commitopts, opts)
-    
+
     # wrap ui.write so diff output can be labeled/colorized
     def wrapwrite(orig, *args, **kw):
         label = kw.pop('label', '')
     # Check for shelve file at old location first
     if os.path.isfile(repo.join('shelve')):
         ui.status('default\n')
-    
+
     # Now go through all the files in the shelves folder and list them out
     dirname = repo.join('shelves')
     if os.path.isdir(dirname):
-        for filename in os.listdir(repo.join('shelves')):
+        for filename in sorted(os.listdir(repo.join('shelves'))):
             ui.status(filename + '\n')
-    
+
 def unshelve(ui, repo, **opts):
     '''restore shelved changes'''
 
     if opts['list']:
         listshelves(ui,repo)
         return
-        
+
     try:
         patch_diff = repo.opener(shelfpath).read()
         fp = cStringIO.StringIO(patch_diff)
                     else:
                         ui.status('restoring backup files\n')
                         for realname, tmpname in backups.iteritems():
-                            ui.debug('restoring %r to %r\n' % 
+                            ui.debug('restoring %r to %r\n' %
                                      (tmpname, realname))
                             util.copyfile(tmpname, repo.wjoin(realname))
             finally:
     "unshelve":
         (unshelve,
          [('i', 'inspect', None, _('inspect shelved changes only')),
-          ('f', 'force', None, 
+          ('f', 'force', None,
            _('proceed even if patches do not unshelve cleanly')),
            ('n', 'name', '',
             _('unshelve changes from specified shelf name')),