[PATCH 2 of 3 RFC] flags: introduce a command for manipulating file flags (issue2020)

timeless timeless at gmail.com
Wed Jul 6 21:46:48 EDT 2016


so, we could do:

hg chmod -flag +executable paths

It could also be done using a command other than `chmod` once you
switch to using `-flag` (or similar) as the flag.

In this model, you could also support octal as the parser would come
from `-flag` instead of having to explain to the command parser that
you have arbitrary arguments that are either files or attributes.

On Sun, Jul 3, 2016 at 6:00 PM, Matt Harbison <mharbison72 at gmail.com> wrote:
> # HG changeset patch
> # User Matt Harbison <matt_harbison at yahoo.com>
> # Date 1467573637 14400
> #      Sun Jul 03 15:20:37 2016 -0400
> # Node ID e6da62c6d4b29f29a1cad36e72d98d9208186e51
> # Parent  e9fce4275ce6b26b941f47044744015e90367e7b
> flags: introduce a command for manipulating file flags (issue2020)
>
> The need here is for tweaking the executable bit on Windows, but rather than
> making this a platform specific command, Unix is supported with a simple
> `chmod`, allowing the existing flag handling to continue unchanged.  I didn't
> think a debug command was appropriate, because we probably don't want most users
> to know that is a thing.
>
> I suppose we could move the chmod into the flagstate class, but there's also the
> issue of needing to setup the transaction here, so I'm not sure that it is worth
> it.
>
> In the workingctx class, the flagstate file is consulted first.  Only if there
> is not an entry for a file does it fall back to the previous manifest based
> code to fabricate the flags.  This is simpler than only keeping an entry if it
> is the opposite of the manifest.  The file is discarded for both `hg update -C`
> and `hg commit`.  This ignores revert for now, because there are some issues
> with that.  This can also probably be used by record, since
> test-commit-interactive-record.t is basically broken on Windows without it.
>
> I hate this UI.  I was originally thinking `hg chmod [+|-]x` without thinking
> that the args need to start with '-', so it ends up being '-x' sets the bit
> whereas `chmod -x` clears it.  Yuck.  And the name isn't very intuitive for a
> user.  So bikeshed away, I'm sure someone has a better idea.  As mentioned, I
> was also trying to not block a '-l' and '-f' or some such thing to manipulate
> symlinks in the future, though I don't currently have an interest in that.
>
> I'll integrate the tests with something existing once this starts winding down.
>
> diff --git a/mercurial/commands.py b/mercurial/commands.py
> --- a/mercurial/commands.py
> +++ b/mercurial/commands.py
> @@ -3970,6 +3970,50 @@
>
>      return ret
>
> + at command('^flags',
> +    [('n', 'normal', None, _('clear the executable bit')),
> +    ('x', 'executable', None, _('set the executable bit'))
> +    ] + walkopts,
> +    _('FILE...'),
> +    inferrepo=True)
> +def flags(ui, repo, *pats, **opts):
> +    """modify the flags on the given files
> +
> +    Set or clear the executable bit for the named files.  On a filesystem that
> +    tracks the executable bit, this is equivalent to ``chmod +x FILE``.
> +
> +    Returns 0 on success.
> +    """
> +    isexec = opts.get('executable')
> +    isnorm = opts.get('normal')
> +
> +    if (isexec and isnorm) or (not isexec and not isnorm):
> +        raise error.Abort(_("exactly one of -x and -n must be specified"))
> +
> +    wctx = repo[None]
> +    m = scmutil.match(wctx, pats, opts)
> +
> +    if util.checkexec(repo.root):
> +        for f in m.files():
> +            repo.wvfs.chmod(f, isexec and 0o755 or 0o644 )
> +    else:
> +        wlock = lock = tr = None
> +        try:
> +            wlock = repo.wlock()
> +            lock = repo.lock()
> +            fs = wctx.flagstate
> +
> +            tr = repo.transaction('flags')
> +            for f in m.files():
> +                fs[f] = isexec and 'x' or ''
> +
> +            fs.write(tr)
> +            tr.close()
> +        finally:
> +            lockmod.release(wlock, lock, tr)
> +
> +    return 0
> +
>  @command('^forget', walkopts, _('[OPTION]... FILE...'), inferrepo=True)
>  def forget(ui, repo, *pats, **opts):
>      """forget the specified files on the next commit
> diff --git a/mercurial/context.py b/mercurial/context.py
> --- a/mercurial/context.py
> +++ b/mercurial/context.py
> @@ -1188,6 +1188,7 @@
>          # Create a fallback function for getting file flags when the
>          # filesystem doesn't support them
>
> +        fs = scmutil.flagstate(self._repo)
>          copiesget = self._repo.dirstate.copies().get
>          parents = self.parents()
>          if len(parents) < 2:
> @@ -1195,6 +1196,8 @@
>              man = parents[0].manifest()
>              def func(f):
>                  f = copiesget(f, f)
> +                if f in fs:
> +                    return fs[f]
>                  return man.flags(f)
>          else:
>              # merges are tricky: we try to reconstruct the unstored
> @@ -1205,6 +1208,8 @@
>
>              def func(f):
>                  f = copiesget(f, f) # may be wrong for merges with copies
> +                if f in fs:
> +                    return fs[f]
>                  fl1, fl2, fla = m1.flags(f), m2.flags(f), ma.flags(f)
>                  if fl1 == fl2:
>                      return fl1
> @@ -1408,6 +1413,10 @@
>              p = p[:-1]
>          return [changectx(self._repo, x) for x in p]
>
> +    @propertycache
> +    def flagstate(self):
> +        return scmutil.flagstate(self._repo)
> +
>      def filectx(self, path, filelog=None):
>          """get a file context from the working directory"""
>          return workingfilectx(self._repo, path, workingctx=self,
> diff --git a/mercurial/hg.py b/mercurial/hg.py
> --- a/mercurial/hg.py
> +++ b/mercurial/hg.py
> @@ -691,6 +691,7 @@
>      """forcibly switch the working directory to node, clobbering changes"""
>      stats = updaterepo(repo, node, True)
>      util.unlinkpath(repo.join('graftstate'), ignoremissing=True)
> +    repo[None].flagstate.discard()
>      if show_stats:
>          _showstats(repo, stats, quietempty)
>      return stats[3] > 0
> diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py
> --- a/mercurial/localrepo.py
> +++ b/mercurial/localrepo.py
> @@ -1638,6 +1638,7 @@
>              # update bookmarks, dirstate and mergestate
>              bookmarks.update(self, [p1, p2], ret)
>              cctx.markcommitted(ret)
> +            wctx.flagstate.discard()
>              ms.reset()
>              tr.close()
>
> diff --git a/tests/test-flags2.t b/tests/test-flags2.t
> new file mode 100644
> --- /dev/null
> +++ b/tests/test-flags2.t
> @@ -0,0 +1,66 @@
> +  $ hg init foo
> +  $ cd foo
> +  $ echo noexec > noexec.txt
> +  $ echo noexec > noexec2.txt
> +  $ hg ci -Aqm "no exec"
> +  $ hg files -v
> +           7   noexec.txt
> +           7   noexec2.txt
> +
> +
> +chmod +x, test and commit
> +
> +  $ hg flags -x noexec.txt
> +  $ hg files -v
> +           7 x noexec.txt
> +           7   noexec2.txt
> +
> +  $ hg diff --git
> +  diff --git a/noexec.txt b/noexec.txt
> +  old mode 100644
> +  new mode 100755
> +
> +  $ hg ci -m "set exec"
> +  $ hg files -v
> +           7 x noexec.txt
> +           7   noexec2.txt
> +  $ hg diff --git
> +  $ hg diff -r '.^' --git
> +  diff --git a/noexec.txt b/noexec.txt
> +  old mode 100644
> +  new mode 100755
> +  $ hg manifest --debug
> +  5bb662b3917ab12c167093eb2fa379a1b63142c3 755 * noexec.txt
> +  5bb662b3917ab12c167093eb2fa379a1b63142c3 644   noexec2.txt
> +
> +
> +chmod -x, test and commit
> +
> +#  $ sleep 2
> +#  $ touch noexec.txt
> +
> +  $ hg flags -n noexec.txt
> +  $ hg files -v
> +           7   noexec.txt
> +           7   noexec2.txt
> +  $ hg diff --git
> +  diff --git a/noexec.txt b/noexec.txt
> +  old mode 100755
> +  new mode 100644
> +  $ hg ci -m "clear exec"
> +  $ hg files -v
> +           7   noexec.txt
> +           7   noexec2.txt
> +
> +  $ hg flags -x noexec.txt
> +  $ hg files -v
> +           7 x noexec.txt
> +           7   noexec2.txt
> +
> +Update -C to nuke it
> +  $ hg update -C
> +  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
> +  $ hg files -v
> +           7   noexec.txt
> +           7   noexec2.txt
> +
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel at mercurial-scm.org
> https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


More information about the Mercurial-devel mailing list