hgext/largefiles/overrides.py
changeset 15792 7cbba3adabc7
parent 15788 07b6af9076b4
child 15794 0d91211dd12f
--- a/hgext/largefiles/overrides.py	Sun Jan 08 18:15:54 2012 +0100
+++ b/hgext/largefiles/overrides.py	Sat Jan 07 12:42:54 2012 +0100
@@ -20,6 +20,8 @@
 import lfutil
 import lfcommands
 
+# -- Utility functions: commonly/repeatedly needed functionality ---------------
+
 def installnormalfilesmatchfn(manifest):
     '''overrides scmutil.match so that the matcher it returns will ignore all
     largefiles'''
@@ -51,13 +53,7 @@
     restore matchfn to reverse'''
     scmutil.match = getattr(scmutil.match, 'oldmatch', scmutil.match)
 
-# -- Wrappers: modify existing commands --------------------------------
-
-# Add works by going through the files that the user wanted to add and
-# checking if they should be added as largefiles. Then it makes a new
-# matcher which matches only the normal files and runs the original
-# version of add.
-def override_add(orig, ui, repo, *pats, **opts):
+def add_largefiles(ui, repo, *pats, **opts):
     large = opts.pop('large', None)
     lfsize = lfutil.getminsize(
         ui, lfutil.islfilesrepo(repo), opts.pop('lfsize', None))
@@ -117,19 +113,9 @@
                     if f in m.files()]
     finally:
         wlock.release()
-
-    installnormalfilesmatchfn(repo[None].manifest())
-    result = orig(ui, repo, *pats, **opts)
-    restorematchfn()
-
-    return (result == 1 or bad) and 1 or 0
+    return bad
 
-def override_remove(orig, ui, repo, *pats, **opts):
-    manifest = repo[None].manifest()
-    installnormalfilesmatchfn(manifest)
-    orig(ui, repo, *pats, **opts)
-    restorematchfn()
-
+def remove_largefiles(ui, repo, *pats, **opts):
     after = opts.get('after')
     if not pats and not after:
         raise util.Abort(_('no files specified'))
@@ -139,6 +125,7 @@
         s = repo.status(match=m, clean=True)
     finally:
         repo.lfstatus = False
+    manifest = repo[None].manifest()
     modified, added, deleted, clean = [[f for f in list
                                         if lfutil.standin(f) in manifest]
                                        for list in [s[0], s[1], s[3], s[6]]]
@@ -167,21 +154,48 @@
         lfdirstate = lfutil.openlfdirstate(ui, repo)
         for f in remove:
             if not after:
-                os.unlink(repo.wjoin(f))
+                # If this is being called by addremove, notify the user that we
+                # are removing the file.
+                if getattr(repo, "_isaddremove", False):
+                    ui.status(_('removing %s\n' % f))
+                if os.path.exists(repo.wjoin(f)):
+                    os.unlink(repo.wjoin(f))
                 currentdir = os.path.split(f)[0]
                 while currentdir and not os.listdir(repo.wjoin(currentdir)):
                     os.rmdir(repo.wjoin(currentdir))
                     currentdir = os.path.split(currentdir)[0]
             lfdirstate.remove(f)
         lfdirstate.write()
-
         forget = [lfutil.standin(f) for f in forget]
         remove = [lfutil.standin(f) for f in remove]
         lfutil.repo_forget(repo, forget)
-        lfutil.repo_remove(repo, remove, unlink=True)
+        # If this is being called by addremove, let the original addremove
+        # function handle this.
+        if not getattr(repo, "_isaddremove", False):
+            lfutil.repo_remove(repo, remove, unlink=True)
     finally:
         wlock.release()
 
+# -- Wrappers: modify existing commands --------------------------------
+
+# Add works by going through the files that the user wanted to add and
+# checking if they should be added as largefiles. Then it makes a new
+# matcher which matches only the normal files and runs the original
+# version of add.
+def override_add(orig, ui, repo, *pats, **opts):
+    bad = add_largefiles(ui, repo, *pats, **opts)
+    installnormalfilesmatchfn(repo[None].manifest())
+    result = orig(ui, repo, *pats, **opts)
+    restorematchfn()
+
+    return (result == 1 or bad) and 1 or 0
+
+def override_remove(orig, ui, repo, *pats, **opts):
+    installnormalfilesmatchfn(repo[None].manifest())
+    orig(ui, repo, *pats, **opts)
+    restorematchfn()
+    remove_largefiles(ui, repo, *pats, **opts)
+
 def override_status(orig, ui, repo, *pats, **opts):
     try:
         repo.lfstatus = True
@@ -851,26 +865,29 @@
             ui.status(_('largefiles: %d to upload\n') % len(toupload))
 
 def override_addremove(orig, ui, repo, *pats, **opts):
-    # Check if the parent or child has largefiles; if so, disallow
-    # addremove. If there is a symlink in the manifest then getting
-    # the manifest throws an exception: catch it and let addremove
-    # deal with it.
-    try:
-        manifesttip = set(repo['tip'].manifest())
-    except util.Abort:
-        manifesttip = set()
-    try:
-        manifestworking = set(repo[None].manifest())
-    except util.Abort:
-        manifestworking = set()
+    # Get the list of missing largefiles so we can remove them
+    lfdirstate = lfutil.openlfdirstate(ui, repo)
+    s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [], False,
+        False, False)
+    (unsure, modified, added, removed, missing, unknown, ignored, clean) = s
 
-    # Manifests are only iterable so turn them into sets then union
-    for file in manifesttip.union(manifestworking):
-        if file.startswith(lfutil.shortname):
-            raise util.Abort(
-                _('addremove cannot be run on a repo with largefiles'))
-
-    return orig(ui, repo, *pats, **opts)
+    # Call into the normal remove code, but the removing of the standin, we want
+    # to have handled by original addremove.  Monkey patching here makes sure
+    # we don't remove the standin in the largefiles code, preventing a very
+    # confused state later.
+    repo._isaddremove = True
+    remove_largefiles(ui, repo, *missing, **opts)
+    repo._isaddremove = False
+    # Call into the normal add code, and any files that *should* be added as
+    # largefiles will be
+    add_largefiles(ui, repo, *pats, **opts)
+    # Now that we've handled largefiles, hand off to the original addremove
+    # function to take care of the rest.  Make sure it doesn't do anything with
+    # largefiles by installing a matcher that will ignore them.
+    installnormalfilesmatchfn(repo[None].manifest())
+    result = orig(ui, repo, *pats, **opts)
+    restorematchfn()
+    return result
 
 # Calling purge with --all will cause the largefiles to be deleted.
 # Override repo.status to prevent this from happening.