merge: use separate lists for each action type
authorMads Kiilerich <madski@unity3d.com>
Fri, 28 Feb 2014 02:25:58 +0100
changeset 21545 43eecb4e23f8
parent 21541 6062593d8b06
child 21546 cb0e28d61905
merge: use separate lists for each action type This replaces the grand unified action list that had multiple action types as tuples in one big list. That list was iterated multiple times just to find actions of a specific type. This data model also made some code more convoluted than necessary. Instead we now store actions as a tuple of lists. Using multiple lists gives a bit of cut'n'pasted code but also enables other optimizations. This patch uses 'if True:' to preserve indentations and help reviewing. It also limits the number of conflicts with other pending patches. It can trivially be cleaned up later.
hgext/largefiles/overrides.py
mercurial/merge.py
--- a/hgext/largefiles/overrides.py	Fri May 23 13:10:31 2014 -0700
+++ b/hgext/largefiles/overrides.py	Fri Feb 28 02:25:58 2014 +0100
@@ -410,14 +410,13 @@
     if overwrite:
         return actions
 
-    removes = set(a[0] for a in actions if a[1] == 'r')
-    processed = []
+    removes = set(a[0] for a in actions['r'])
 
-    for action in actions:
-        f, m, args, msg = action
-
+    newglist = []
+    for action in actions['g']:
+        f, args, msg = action
         splitstandin = f and lfutil.splitstandin(f)
-        if (m == "g" and splitstandin is not None and
+        if (splitstandin is not None and
             splitstandin in p1 and splitstandin not in removes):
             # Case 1: normal file in the working copy, largefile in
             # the second parent
@@ -427,12 +426,11 @@
                     'use (l)argefile or keep (n)ormal file?'
                     '$$ &Largefile $$ &Normal file') % lfile
             if repo.ui.promptchoice(msg, 0) == 0:
-                processed.append((lfile, "r", None, msg))
-                processed.append((standin, "g", (p2.flags(standin),), msg))
+                actions['r'].append((lfile, None, msg))
+                newglist.append((standin, (p2.flags(standin),), msg))
             else:
-                processed.append((standin, "r", None, msg))
-        elif (m == "g" and
-            lfutil.standin(f) in p1 and lfutil.standin(f) not in removes):
+                actions['r'].append((standin, None, msg))
+        elif lfutil.standin(f) in p1 and lfutil.standin(f) not in removes:
             # Case 2: largefile in the working copy, normal file in
             # the second parent
             standin = lfutil.standin(f)
@@ -441,14 +439,17 @@
                     'keep (l)argefile or use (n)ormal file?'
                     '$$ &Largefile $$ &Normal file') % lfile
             if repo.ui.promptchoice(msg, 0) == 0:
-                processed.append((lfile, "r", None, msg))
+                actions['r'].append((lfile, None, msg))
             else:
-                processed.append((standin, "r", None, msg))
-                processed.append((lfile, "g", (p2.flags(lfile),), msg))
+                actions['r'].append((standin, None, msg))
+                newglist.append((lfile, (p2.flags(lfile),), msg))
         else:
-            processed.append(action)
+            newglist.append(action)
 
-    return processed
+    newglist.sort()
+    actions['g'] = newglist
+
+    return actions
 
 # Override filemerge to prompt the user about how they wish to merge
 # largefiles. This will handle identical edits without prompting the user.
--- a/mercurial/merge.py	Fri May 23 13:10:31 2014 -0700
+++ b/mercurial/merge.py	Fri Feb 28 02:25:58 2014 +0100
@@ -331,62 +331,44 @@
     as removed.
     """
 
-    actions = []
-    state = branchmerge and 'r' or 'f'
+    ractions = []
+    factions = xactions = []
+    if branchmerge:
+        xactions = ractions
     for f in wctx.deleted():
         if f not in mctx:
-            actions.append((f, state, None, "forget deleted"))
+            xactions.append((f, None, "forget deleted"))
 
     if not branchmerge:
         for f in wctx.removed():
             if f not in mctx:
-                actions.append((f, "f", None, "forget removed"))
+                factions.append((f, None, "forget removed"))
 
-    return actions
+    return ractions, factions
 
 def _checkcollision(repo, wmf, actions):
     # build provisional merged manifest up
     pmmf = set(wmf)
 
-    def addop(f, args):
-        pmmf.add(f)
-    def removeop(f, args):
-        pmmf.discard(f)
-    def nop(f, args):
-        pass
-
-    def renamemoveop(f, args):
-        f2, flags = args
-        pmmf.discard(f2)
-        pmmf.add(f)
-    def renamegetop(f, args):
-        f2, flags = args
-        pmmf.add(f)
-    def mergeop(f, args):
-        f1, f2, fa, move, anc = args
-        if move:
-            pmmf.discard(f1)
-        pmmf.add(f)
-
-    opmap = {
-        "a": addop,
-        "dm": renamemoveop,
-        "dg": renamegetop,
-        "dr": nop,
-        "e": nop,
-        "k": nop,
-        "f": addop, # untracked file should be kept in working directory
-        "g": addop,
-        "m": mergeop,
-        "r": removeop,
-        "rd": nop,
-        "cd": addop,
-        "dc": addop,
-    }
-    for f, m, args, msg in actions:
-        op = opmap.get(m)
-        assert op, m
-        op(f, args)
+    if actions:
+        # k, dr, e and rd are no-op
+        for m in 'a', 'f', 'g', 'cd', 'dc':
+            for f, args, msg in actions[m]:
+                pmmf.add(f)
+        for f, args, msg in actions['r']:
+            pmmf.discard(f)
+        for f, args, msg in actions['dm']:
+            f2, flags = args
+            pmmf.discard(f2)
+            pmmf.add(f)
+        for f, args, msg in actions['dg']:
+            f2, flags = args
+            pmmf.add(f)
+        for f, args, msg in actions['m']:
+            f1, f2, fa, move, anc = args
+            if move:
+                pmmf.discard(f1)
+            pmmf.add(f)
 
     # check case-folding collision in provisional merged manifest
     foldmap = {}
@@ -407,7 +389,8 @@
     acceptremote = accept the incoming changes without prompting
     """
 
-    actions, copy, movewithdir = [], {}, {}
+    actions = dict((m, []) for m in 'a f g cd dc r dm dg m dr e rd k'.split())
+    copy, movewithdir = {}, {}
 
     # manifests fetched in order are going to be faster, so prime the caches
     [x.manifest() for x in
@@ -417,9 +400,9 @@
         ret = copies.mergecopies(repo, wctx, p2, pa)
         copy, movewithdir, diverge, renamedelete = ret
         for of, fl in diverge.iteritems():
-            actions.append((of, "dr", (fl,), "divergent renames"))
+            actions['dr'].append((of, (fl,), "divergent renames"))
         for of, fl in renamedelete.iteritems():
-            actions.append((of, "rd", (fl,), "rename and delete"))
+            actions['rd'].append((of, (fl,), "rename and delete"))
 
     repo.ui.note(_("resolving manifests\n"))
     repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n"
@@ -471,50 +454,50 @@
             fla = ma.flags(fa)
             nol = 'l' not in fl1 + fl2 + fla
             if n2 == a and fl2 == fla:
-                actions.append((f, "k", (), "keep")) # remote unchanged
+                actions['k'].append((f, (), "keep")) # remote unchanged
             elif n1 == a and fl1 == fla: # local unchanged - use remote
                 if n1 == n2: # optimization: keep local content
-                    actions.append((f, "e", (fl2,), "update permissions"))
+                    actions['e'].append((f, (fl2,), "update permissions"))
                 else:
-                    actions.append((f, "g", (fl2,), "remote is newer"))
+                    actions['g'].append((f, (fl2,), "remote is newer"))
             elif nol and n2 == a: # remote only changed 'x'
-                actions.append((f, "e", (fl2,), "update permissions"))
+                actions['e'].append((f, (fl2,), "update permissions"))
             elif nol and n1 == a: # local only changed 'x'
-                actions.append((f, "g", (fl1,), "remote is newer"))
+                actions['g'].append((f, (fl1,), "remote is newer"))
             else: # both changed something
-                actions.append((f, "m", (f, f, fa, False, pa.node()),
+                actions['m'].append((f, (f, f, fa, False, pa.node()),
                                "versions differ"))
         elif f in copied: # files we'll deal with on m2 side
             pass
         elif n1 and f in movewithdir: # directory rename, move local
             f2 = movewithdir[f]
-            actions.append((f2, "dm", (f, fl1),
+            actions['dm'].append((f2, (f, fl1),
                             "remote directory rename - move from " + f))
         elif n1 and f in copy:
             f2 = copy[f]
-            actions.append((f, "m", (f, f2, f2, False, pa.node()),
+            actions['m'].append((f, (f, f2, f2, False, pa.node()),
                             "local copied/moved from " + f2))
         elif n1 and f in ma: # clean, a different, no remote
             if n1 != ma[f]:
                 if acceptremote:
-                    actions.append((f, "r", None, "remote delete"))
+                    actions['r'].append((f, None, "remote delete"))
                 else:
-                    actions.append((f, "cd", None, "prompt changed/deleted"))
+                    actions['cd'].append((f, None, "prompt changed/deleted"))
             elif n1[20:] == "a": # added, no remote
-                actions.append((f, "f", None, "remote deleted"))
+                actions['f'].append((f, None, "remote deleted"))
             else:
-                actions.append((f, "r", None, "other deleted"))
+                actions['r'].append((f, None, "other deleted"))
         elif n2 and f in movewithdir:
             f2 = movewithdir[f]
-            actions.append((f2, "dg", (f, fl2),
+            actions['dg'].append((f2, (f, fl2),
                             "local directory rename - get from " + f))
         elif n2 and f in copy:
             f2 = copy[f]
             if f2 in m2:
-                actions.append((f, "m", (f2, f, f2, False, pa.node()),
+                actions['m'].append((f, (f2, f, f2, False, pa.node()),
                                 "remote copied from " + f2))
             else:
-                actions.append((f, "m", (f2, f, f2, True, pa.node()),
+                actions['m'].append((f, (f2, f, f2, True, pa.node()),
                                 "remote moved from " + f2))
         elif n2 and f not in ma:
             # local unknown, remote created: the logic is described by the
@@ -530,17 +513,17 @@
             # Checking whether the files are different is expensive, so we
             # don't do that when we can avoid it.
             if force and not branchmerge:
-                actions.append((f, "g", (fl2,), "remote created"))
+                actions['g'].append((f, (fl2,), "remote created"))
             else:
                 different = _checkunknownfile(repo, wctx, p2, f)
                 if force and branchmerge and different:
                     # FIXME: This is wrong - f is not in ma ...
-                    actions.append((f, "m", (f, f, f, False, pa.node()),
+                    actions['m'].append((f, (f, f, f, False, pa.node()),
                                     "remote differs from untracked local"))
                 elif not force and different:
                     aborts.append((f, "ud"))
                 else:
-                    actions.append((f, "g", (fl2,), "remote created"))
+                    actions['g'].append((f, (fl2,), "remote created"))
         elif n2 and n2 != ma[f]:
             different = _checkunknownfile(repo, wctx, p2, f)
             if not force and different:
@@ -548,10 +531,10 @@
             else:
                 # if different: old untracked f may be overwritten and lost
                 if acceptremote:
-                    actions.append((f, "g", (m2.flags(f),),
+                    actions['g'].append((f, (m2.flags(f),),
                                    "remote recreating"))
                 else:
-                    actions.append((f, "dc", (m2.flags(f),),
+                    actions['dc'].append((f, (m2.flags(f),),
                                    "prompt deleted/changed"))
 
     for f, m in sorted(aborts):
@@ -566,18 +549,12 @@
         # check collision between files only in p2 for clean update
         if (not branchmerge and
             (force or not wctx.dirty(missing=True, branch=False))):
-            _checkcollision(repo, m2, [])
+            _checkcollision(repo, m2, None)
         else:
             _checkcollision(repo, m1, actions)
 
     return actions
 
-actionpriority = dict((m, p) for p, m in enumerate(
-    ['r', 'f', 'g', 'a', 'k', 'm', 'dm', 'dg', 'dr', 'cd', 'dc', 'rd', 'e']))
-
-def actionkey(a):
-    return actionpriority[a[1]], a
-
 def batchremove(repo, actions):
     """apply removes to the working directory
 
@@ -588,7 +565,7 @@
     wjoin = repo.wjoin
     audit = repo.wopener.audit
     i = 0
-    for f, m, args, msg in actions:
+    for f, args, msg in actions:
         repo.ui.debug(" %s: %s -> r\n" % (f, msg))
         if True:
             if verbose:
@@ -617,7 +594,7 @@
     fctx = mctx.filectx
     wwrite = repo.wwrite
     i = 0
-    for f, m, args, msg in actions:
+    for f, args, msg in actions:
         repo.ui.debug(" %s: %s -> g\n" % (f, msg))
         if True:
             if verbose:
@@ -644,12 +621,12 @@
     ms = mergestate(repo)
     ms.reset(wctx.p1().node(), mctx.node())
     moves = []
-    actions.sort(key=actionkey)
+    for m, l in actions.items():
+        l.sort()
 
     # prescan for merges
-    for a in actions:
-        f, m, args, msg = a
-        if m == "m": # merge
+    for f, args, msg in actions['m']:
+        if True:
             f1, f2, fa, move, anc = args
             if f == '.hgsubstate': # merged internally
                 continue
@@ -677,55 +654,50 @@
             audit(f)
             util.unlinkpath(repo.wjoin(f))
 
-    numupdates = len([a for a in actions if a[1] != 'k'])
-    workeractions = [a for a in actions if a[1] in 'gr']
-    updateactions = [a for a in workeractions if a[1] == 'g']
-    updated = len(updateactions)
-    removeactions = [a for a in workeractions if a[1] == 'r']
-    removed = len(removeactions)
-    actions = [a for a in actions if a[1] not in 'gr']
+    numupdates = sum(len(l) for m, l in actions.items() if m != 'k')
 
-    hgsub = [a[1] for a in workeractions if a[0] == '.hgsubstate']
-    if hgsub and hgsub[0] == 'r':
+    if [a for a in actions['r'] if a[0] == '.hgsubstate']:
         subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
 
     # remove in parallel (must come first)
     z = 0
-    prog = worker.worker(repo.ui, 0.001, batchremove, (repo,), removeactions)
+    prog = worker.worker(repo.ui, 0.001, batchremove, (repo,), actions['r'])
     for i, item in prog:
         z += i
         progress(_updating, z, item=item, total=numupdates, unit=_files)
+    removed = len(actions['r'])
 
     # get in parallel
-    prog = worker.worker(repo.ui, 0.001, batchget, (repo, mctx), updateactions)
+    prog = worker.worker(repo.ui, 0.001, batchget, (repo, mctx), actions['g'])
     for i, item in prog:
         z += i
         progress(_updating, z, item=item, total=numupdates, unit=_files)
+    updated = len(actions['g'])
 
-    if hgsub and hgsub[0] == 'g':
+    if [a for a in actions['g'] if a[0] == '.hgsubstate']:
         subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
 
-    for f, m, args, msg in actions:
+    if True:
 
         # forget (manifest only, just log it) (must come first)
-        if m == "f":
+        for f, args, msg in actions['f']:
             repo.ui.debug(" %s: %s -> f\n" % (f, msg))
             z += 1
             progress(_updating, z, item=f, total=numupdates, unit=_files)
 
         # re-add (manifest only, just log it)
-        elif m == "a":
+        for f, args, msg in actions['a']:
             repo.ui.debug(" %s: %s -> a\n" % (f, msg))
             z += 1
             progress(_updating, z, item=f, total=numupdates, unit=_files)
 
         # keep (noop, just log it)
-        elif m == "k":
+        for f, args, msg in actions['k']:
             repo.ui.debug(" %s: %s -> k\n" % (f, msg))
             # no progress
 
         # merge
-        elif m == "m":
+        for f, args, msg in actions['m']:
             repo.ui.debug(" %s: %s -> m\n" % (f, msg))
             z += 1
             progress(_updating, z, item=f, total=numupdates, unit=_files)
@@ -745,7 +717,7 @@
                     merged += 1
 
         # directory rename, move local
-        elif m == "dm":
+        for f, args, msg in actions['dm']:
             repo.ui.debug(" %s: %s -> dm\n" % (f, msg))
             z += 1
             progress(_updating, z, item=f, total=numupdates, unit=_files)
@@ -757,7 +729,7 @@
             updated += 1
 
         # local directory rename, get
-        elif m == "dg":
+        for f, args, msg in actions['dg']:
             repo.ui.debug(" %s: %s -> dg\n" % (f, msg))
             z += 1
             progress(_updating, z, item=f, total=numupdates, unit=_files)
@@ -767,7 +739,7 @@
             updated += 1
 
         # divergent renames
-        elif m == "dr":
+        for f, args, msg in actions['dr']:
             repo.ui.debug(" %s: %s -> dr\n" % (f, msg))
             z += 1
             progress(_updating, z, item=f, total=numupdates, unit=_files)
@@ -778,7 +750,7 @@
                 repo.ui.warn(" %s\n" % nf)
 
         # rename and delete
-        elif m == "rd":
+        for f, args, msg in actions['rd']:
             repo.ui.debug(" %s: %s -> rd\n" % (f, msg))
             z += 1
             progress(_updating, z, item=f, total=numupdates, unit=_files)
@@ -789,7 +761,7 @@
                 repo.ui.warn(" %s\n" % nf)
 
         # exec
-        elif m == "e":
+        for f, args, msg in actions['e']:
             repo.ui.debug(" %s: %s -> e\n" % (f, msg))
             z += 1
             progress(_updating, z, item=f, total=numupdates, unit=_files)
@@ -818,132 +790,127 @@
             (wctx, mctx, _(' and ').join(str(anc) for anc in ancestors)))
 
         # Call for bids
-        fbids = {} # mapping filename to list af action bids
+        fbids = {} # mapping filename to bids (action method to list af actions)
         for ancestor in ancestors:
             repo.ui.note(_('\ncalculating bids for ancestor %s\n') % ancestor)
             actions = manifestmerge(repo, wctx, mctx, ancestor,
                                     branchmerge, force,
                                     partial, acceptremote, followcopies)
-            for a in sorted(actions, key=lambda a: (a[1], a)):
-                f, m, args, msg = a
-                repo.ui.debug(' %s: %s -> %s\n' % (f, msg, m))
-                if f in fbids:
-                    fbids[f].append(a)
-                else:
-                    fbids[f] = [a]
+            for m, l in sorted(actions.items()):
+                for a in l:
+                    f, args, msg = a
+                    repo.ui.debug(' %s: %s -> %s\n' % (f, msg, m))
+                    if f in fbids:
+                        d = fbids[f]
+                        if m in d:
+                            d[m].append(a)
+                        else:
+                            d[m] = [a]
+                    else:
+                        fbids[f] = {m: [a]}
 
         # Pick the best bid for each file
         repo.ui.note(_('\nauction for merging merge bids\n'))
-        actions = []
-        for f, bidsl in sorted(fbids.items()):
+        actions = dict((m, []) for m in actions.keys())
+        for f, bids in sorted(fbids.items()):
+            # bids is a mapping from action method to list af actions
             # Consensus?
-            a0 = bidsl[0]
-            if util.all(a == a0 for a in bidsl[1:]): # len(bidsl) is > 1
-                repo.ui.note(" %s: consensus for %s\n" % (f, a0[1]))
-                actions.append(a0)
-                continue
-            # Group bids by kind of action
-            bids = {}
-            for a in bidsl:
-                m = a[1]
-                if m in bids:
-                    bids[m].append(a)
-                else:
-                    bids[m] = [a]
+            if len(bids) == 1: # all bids are the same kind of method
+                m, l = bids.items()[0]
+                if util.all(a == l[0] for a in l[1:]): # len(bids) is > 1
+                    repo.ui.note(" %s: consensus for %s\n" % (f, m))
+                    actions[m].append(l[0])
+                    continue
             # If keep is an option, just do it.
             if "k" in bids:
                 repo.ui.note(" %s: picking 'keep' action\n" % f)
-                actions.append(bids["k"][0])
+                actions['k'].append(bids["k"][0])
                 continue
-            # If all gets agree [how could they not?], just do it.
+            # If there are gets and they all agree [how could they not?], do it.
             if "g" in bids:
                 ga0 = bids["g"][0]
                 if util.all(a == ga0 for a in bids["g"][1:]):
                     repo.ui.note(" %s: picking 'get' action\n" % f)
-                    actions.append(ga0)
+                    actions['g'].append(ga0)
                     continue
             # TODO: Consider other simple actions such as mode changes
             # Handle inefficient democrazy.
             repo.ui.note(_(' %s: multiple bids for merge action:\n') % f)
-            for _f, m, args, msg in bidsl:
-                repo.ui.note('  %s -> %s\n' % (msg, m))
+            for m, l in sorted(bids.items()):
+                for _f, args, msg in l:
+                    repo.ui.note('  %s -> %s\n' % (msg, m))
             # Pick random action. TODO: Instead, prompt user when resolving
-            a0 = bidsl[0]
+            m, l = bids.items()[0]
             repo.ui.warn(_(' %s: ambiguous merge - picked %s action\n') %
-                         (f, a0[1]))
-            actions.append(a0)
+                         (f, m))
+            actions[m].append(l[0])
             continue
         repo.ui.note(_('end of auction\n\n'))
 
-    # Filter out prompts.
-    newactions, prompts = [], []
-    for a in actions:
-        if a[1] in ("cd", "dc"):
-            prompts.append(a)
-        else:
-            newactions.append(a)
     # Prompt and create actions. TODO: Move this towards resolve phase.
-    for f, m, args, msg in sorted(prompts):
-        if m == "cd":
+    if True:
+        for f, args, msg in actions['cd']:
             if repo.ui.promptchoice(
                 _("local changed %s which remote deleted\n"
                   "use (c)hanged version or (d)elete?"
                   "$$ &Changed $$ &Delete") % f, 0):
-                newactions.append((f, "r", None, "prompt delete"))
+                actions['r'].append((f, None, "prompt delete"))
             else:
-                newactions.append((f, "a", None, "prompt keep"))
-        elif m == "dc":
+                actions['a'].append((f, None, "prompt keep"))
+        del actions['cd'][:]
+
+        for f, args, msg in actions['dc']:
             flags, = args
             if repo.ui.promptchoice(
                 _("remote changed %s which local deleted\n"
                   "use (c)hanged version or leave (d)eleted?"
                   "$$ &Changed $$ &Deleted") % f, 0) == 0:
-                newactions.append((f, "g", (flags,), "prompt recreating"))
-        else: assert False, m
+                actions['g'].append((f, (flags,), "prompt recreating"))
+        del actions['dc'][:]
 
     if wctx.rev() is None:
-        newactions += _forgetremoved(wctx, mctx, branchmerge)
+        ractions, factions = _forgetremoved(wctx, mctx, branchmerge)
+        actions['r'].extend(ractions)
+        actions['f'].extend(factions)
 
-    return newactions
+    return actions
 
 def recordupdates(repo, actions, branchmerge):
     "record merge actions to the dirstate"
-
-    for f, m, args, msg in actions:
-
+    if True:
         # remove (must come first)
-        if m == "r": # remove
+        for f, args, msg in actions['r']:
             if branchmerge:
                 repo.dirstate.remove(f)
             else:
                 repo.dirstate.drop(f)
 
         # forget (must come first)
-        elif m == "f":
+        for f, args, msg in actions['f']:
             repo.dirstate.drop(f)
 
         # re-add
-        elif m == "a":
+        for f, args, msg in actions['a']:
             if not branchmerge:
                 repo.dirstate.add(f)
 
         # exec change
-        elif m == "e":
+        for f, args, msg in actions['e']:
             repo.dirstate.normallookup(f)
 
         # keep
-        elif m == "k":
+        for f, args, msg in actions['k']:
             pass
 
         # get
-        elif m == "g":
+        for f, args, msg in actions['g']:
             if branchmerge:
                 repo.dirstate.otherparent(f)
             else:
                 repo.dirstate.normal(f)
 
         # merge
-        elif m == "m":
+        for f, args, msg in actions['m']:
             f1, f2, fa, move, anc = args
             if branchmerge:
                 # We've done a branch merge, mark this file as merged
@@ -968,7 +935,7 @@
                     repo.dirstate.drop(f1)
 
         # directory rename, move local
-        elif m == "dm":
+        for f, args, msg in actions['dm']:
             f0, flag = args
             if f0 not in repo.dirstate:
                 # untracked file moved
@@ -982,7 +949,7 @@
                 repo.dirstate.drop(f0)
 
         # directory rename, get
-        elif m == "dg":
+        for f, args, msg in actions['dg']:
             f0, flag = args
             if branchmerge:
                 repo.dirstate.add(f)