[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