Patchwork [RFC,WIP] status: add an option to see terse status (WIP)

login
register
mail settings
Submitter Pulkit Goyal
Date May 26, 2017, 9:18 p.m.
Message ID <76fb3bdd547259620eec.1495833481@workspace>
Download mbox | patch
Permalink /patch/20939/
State Changes Requested
Headers show

Comments

Pulkit Goyal - May 26, 2017, 9:18 p.m.
# HG changeset patch
# User Pulkit Goyal <7895pulkit@gmail.com>
# Date 1495832891 -19800
#      Sat May 27 02:38:11 2017 +0530
# Node ID 76fb3bdd547259620eec714c0d50fe1f9da25ebf
# Parent  2b5953a49f1407f825d65b45986d213cb5c79203
status: add an option to see terse status (WIP)

This is a work in progress to implement terse support for hg status. The current
implementation assumes that we need to collapse a certain directory when all the
files inside it has the same status which I think should be the default
behavior. Currently os module is used to list files and do related handling as I
am learning how to use matchers. There is a scope of lot of optimizations in the
above code.

The current implementation lacks things like skipping .hg folder, .pyc files or
any other files which are there and should not be tracked. I think those will be
fixed once I use matcher.

The reason I have send this WIP is to know what should be the behavior of the
output, i.e. is the current behavior okay or we also want to show status like:

M abc/
? abc/

or if say folder abc has 10 files out which only 5 are changed in the dirstate,
rest of them are untouched, and we show terse status as

M abc/

So, I will like to know what kind of behavior we want to support.

Secondly does the current direction/algorithm sounds good?
Augie Fackler - May 28, 2017, 6 p.m.
> On May 26, 2017, at 5:18 PM, Pulkit Goyal <7895pulkit@gmail.com> wrote:
> 
> # HG changeset patch
> # User Pulkit Goyal <7895pulkit@gmail.com>
> # Date 1495832891 -19800
> #      Sat May 27 02:38:11 2017 +0530
> # Node ID 76fb3bdd547259620eec714c0d50fe1f9da25ebf
> # Parent  2b5953a49f1407f825d65b45986d213cb5c79203
> status: add an option to see terse status (WIP)

I’m generally in favor of terse status, but with a quirk: I think it should only be terse by default for ?, which mirrors the Subversion behavior.

> 
> This is a work in progress to implement terse support for hg status. The current
> implementation assumes that we need to collapse a certain directory when all the
> files inside it has the same status which I think should be the default
> behavior. Currently os module is used to list files and do related handling as I
> am learning how to use matchers. There is a scope of lot of optimizations in the
> above code.
> 
> The current implementation lacks things like skipping .hg folder, .pyc files or
> any other files which are there and should not be tracked. I think those will be
> fixed once I use matcher.
> 
> The reason I have send this WIP is to know what should be the behavior of the
> output, i.e. is the current behavior okay or we also want to show status like:
> 
> M abc/
> ? abc/

Are there also clean files in abc? I think “clean” status counts even if its not visible (see above for my hand wave about svn behavior, which experimentally “felt right” to me)

> 
> or if say folder abc has 10 files out which only 5 are changed in the dirstate,
> rest of them are untouched, and we show terse status as
> 
> M abc/
> 
> So, I will like to know what kind of behavior we want to support.
> 
> Secondly does the current direction/algorithm sounds good?
> 
> diff --git a/mercurial/commands.py b/mercurial/commands.py
> --- a/mercurial/commands.py
> +++ b/mercurial/commands.py
> @@ -4663,6 +4663,7 @@
>     ('c', 'clean', None, _('show only files without changes')),
>     ('u', 'unknown', None, _('show only unknown (not tracked) files')),
>     ('i', 'ignored', None, _('show only ignored files')),
> +    ('t', 'terse', None, _('Show the tersed status')),
>     ('n', 'no-status', None, _('hide status prefix')),
>     ('C', 'copies', None, _('show source of copied files')),
>     ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
> @@ -4780,6 +4781,60 @@
>     fmt = '%s' + end
>     showchar = not opts.get('no_status')
> 
> +    if opts.get('terse'):
> +        #visited = []
> +        finalls = [[], [], [], [], [], [], []]
> +        for i in range(0,7):
> +            reslist = []
> +            filedirdict = {}
> +            newlist = []
> +            fileslist = changestates[i][2]
> +            lensuf = len(repo.root + os.path.sep)
> +            for file in fileslist:
> +                par = repo.root + os.path.sep + os.path.dirname(file)
> +                #if par in visited:
> +                #    continue
> +
> +                try:
> +                    filedirdict[par].append(file)
> +                except KeyError:
> +                    filedirdict[par] = [file]
> +
> +            cpdict = filedirdict.copy()
> +            for dirname, fnames in cpdict.iteritems():
> +                #visited.append(dirname)
> +                # This should be replaced with matcher
> +                if len(os.listdir(dirname)) == len(fnames):
> +                    dirpar = os.path.dirname(dirname)
> +                    try:
> +                        filedirdict[dirpar].append(dirname[lensuf:] +
> +                                                os.path.sep)
> +                    except KeyError:
> +                        filedirdict[dirpar] = [dirname[lensuf:]+ os.path.sep]
> +                    filedirdict[dirname] = []
> +                    if dirname != repo.root:
> +                        newlist.append(dirname)
> +
> +            while(len(newlist) != 0):
> +                fname = newlist.pop()
> +                par = os.path.dirname(fname)
> +
> +                if len(os.listdir(par)) == len(filedirdict[par]):
> +                    parparent = os.path.dirname(par)
> +                    try:
> +                        filedirdict[parparent].append(par[lensuf:] + os.path.sep)
> +                    except KeyError:
> +                        filedirdict[parparent] = [par[lensuf:] + os.path.sep]
> +                    filedirdict[par] = []
> +                    if parparent != repo.root:
> +                        newlist.insert(0, parparent)
> +
> +            for dirname, fnames in filedirdict.iteritems():
> +                finalls[i].extend(fnames)
> +
> +        changestates = zip(states, pycompat.iterbytestr('MAR!?IC'), finalls)
> +
> +
>     for state, char, files in changestates:
>         if state in show:
>             label = 'status.' + state
> diff --git a/tests/test-terse-status.t b/tests/test-terse-status.t
> new file mode 100644
> --- /dev/null
> +++ b/tests/test-terse-status.t
> @@ -0,0 +1,73 @@
> +  $ hg init repo
> +  $ cd repo
> +  $ touch a b c
> +  $ hg status
> +  ? a
> +  ? b
> +  ? c
> +
> +This one is not working as its listing .hg as a file, will be fixed when matcher
> +will be used
> +  $ hg status --terse
> +  ? a
> +  ? b
> +  ? c
> +
> +  $ mkdir folder
> +  $ cd folder
> +  $ touch x y z
> +  $ hg status
> +  ? a
> +  ? b
> +  ? c
> +  ? folder/x
> +  ? folder/y
> +  ? folder/z
> +
> +  $ hg status --terse
> +  ? folder/
> +  ? a
> +  ? b
> +  ? c
> +
> +  $ hg add .
> +  adding x
> +  adding y
> +  adding z
> +
> +  $ hg status
> +  A folder/x
> +  A folder/y
> +  A folder/z
> +  ? a
> +  ? b
> +  ? c
> +
> +  $ hg status --terse
> +  A folder/
> +  ? a
> +  ? b
> +  ? c
> +
> +  $ mkdir another
> +  $ cd another
> +  $ touch p q r
> +  $ hg status --terse
> +  A folder/x
> +  A folder/y
> +  A folder/z
> +  ? folder/another/
> +  ? a
> +  ? b
> +  ? c
> +
> +  $ hg add .
> +  adding p
> +  adding q
> +  adding r
> +
> +  $ hg status --terse
> +  A folder/
> +  ? a
> +  ? b
> +  ? c
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel@mercurial-scm.org
> https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Pulkit Goyal - June 7, 2017, 3:54 p.m.
On Sun, May 28, 2017 at 11:30 PM, Augie Fackler <raf@durin42.com> wrote:
>
>> On May 26, 2017, at 5:18 PM, Pulkit Goyal <7895pulkit@gmail.com> wrote:
>>
>> # HG changeset patch
>> # User Pulkit Goyal <7895pulkit@gmail.com>
>> # Date 1495832891 -19800
>> #      Sat May 27 02:38:11 2017 +0530
>> # Node ID 76fb3bdd547259620eec714c0d50fe1f9da25ebf
>> # Parent  2b5953a49f1407f825d65b45986d213cb5c79203
>> status: add an option to see terse status (WIP)
>
> I’m generally in favor of terse status, but with a quirk: I think it should only be terse by default for ?, which mirrors the Subversion behavior.

I was working on this and had a doubt. Shall a folder containing files
with only unknown and ignored status can be tersed as unknown?
An example for this is a folder containing .py and .pyc files from
which .pyc files are ignored.

The similar thing applies for modified, and added files. Do we want to
make all the statuses mutually exclusive to each other while tersing
OR can some of them exist together and we add the behaviour to use
--verbose to consider the ignored files. Can we do something else
also?

In my opinion the first one will be straightforward for users to
understand. The later one might result in some confusion.
Danek Duvall - June 7, 2017, 4:06 p.m.
Pulkit Goyal wrote:

> On Sun, May 28, 2017 at 11:30 PM, Augie Fackler <raf@durin42.com> wrote:
> >
> >> On May 26, 2017, at 5:18 PM, Pulkit Goyal <7895pulkit@gmail.com> wrote:
> >>
> >> # HG changeset patch
> >> # User Pulkit Goyal <7895pulkit@gmail.com>
> >> # Date 1495832891 -19800
> >> #      Sat May 27 02:38:11 2017 +0530
> >> # Node ID 76fb3bdd547259620eec714c0d50fe1f9da25ebf
> >> # Parent  2b5953a49f1407f825d65b45986d213cb5c79203
> >> status: add an option to see terse status (WIP)
> >
> > I’m generally in favor of terse status, but with a quirk: I think it should only be terse by default for ?, which mirrors the Subversion behavior.
> 
> I was working on this and had a doubt. Shall a folder containing files
> with only unknown and ignored status can be tersed as unknown?

The simple answer to this is yes, if for no other reason than one of the
upsides of terse-status is that it doesn't spend forever descending through
directory hierarchies that have no files managed by mercurial.

If I want to see files there which are ignored, I can always turn off the
terseness, and wait.

Of course, that assumes that the set of ignored files and the set of files
managed by mercurial are exclusive, which they needn't be.  In that case,
I'd probably want to see the ignored files if I simply asked to see ignored
files, even if there were no other managed files in that directory.

So it seems to me that the decision to descend should be made based on
whether there are managed files.

Danek

> An example for this is a folder containing .py and .pyc files from
> which .pyc files are ignored.
> 
> The similar thing applies for modified, and added files. Do we want to
> make all the statuses mutually exclusive to each other while tersing
> OR can some of them exist together and we add the behaviour to use
> --verbose to consider the ignored files. Can we do something else
> also?
> 
> In my opinion the first one will be straightforward for users to
> understand. The later one might result in some confusion.
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel@mercurial-scm.org
> https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Augie Fackler - June 7, 2017, 5:58 p.m.
> On Jun 7, 2017, at 12:06, Danek Duvall <danek.duvall@oracle.com> wrote:
> 
> Pulkit Goyal wrote:
> 
>> On Sun, May 28, 2017 at 11:30 PM, Augie Fackler <raf@durin42.com> wrote:
>>> 
>>>> On May 26, 2017, at 5:18 PM, Pulkit Goyal <7895pulkit@gmail.com> wrote:
>>>> 
>>>> # HG changeset patch
>>>> # User Pulkit Goyal <7895pulkit@gmail.com>
>>>> # Date 1495832891 -19800
>>>> #      Sat May 27 02:38:11 2017 +0530
>>>> # Node ID 76fb3bdd547259620eec714c0d50fe1f9da25ebf
>>>> # Parent  2b5953a49f1407f825d65b45986d213cb5c79203
>>>> status: add an option to see terse status (WIP)
>>> 
>>> I’m generally in favor of terse status, but with a quirk: I think it should only be terse by default for ?, which mirrors the Subversion behavior.
>> 
>> I was working on this and had a doubt. Shall a folder containing files
>> with only unknown and ignored status can be tersed as unknown?
> 
> The simple answer to this is yes, if for no other reason than one of the
> upsides of terse-status is that it doesn't spend forever descending through
> directory hierarchies that have no files managed by mercurial.

I'm conflicted. In Subversion, the answer would be no, but svn:ignore isn't recursive, and svn tracks directories, so it's not a great comparison. Let's start with following Danek's instinct and see how it feels?

> 
> If I want to see files there which are ignored, I can always turn off the
> terseness, and wait.
> 
> Of course, that assumes that the set of ignored files and the set of files
> managed by mercurial are exclusive, which they needn't be.  In that case,
> I'd probably want to see the ignored files if I simply asked to see ignored
> files, even if there were no other managed files in that directory.
> 
> So it seems to me that the decision to descend should be made based on
> whether there are managed files.
> 
> Danek
> 
>> An example for this is a folder containing .py and .pyc files from
>> which .pyc files are ignored.
>> 
>> The similar thing applies for modified, and added files. Do we want to
>> make all the statuses mutually exclusive to each other while tersing
>> OR can some of them exist together and we add the behaviour to use
>> --verbose to consider the ignored files. Can we do something else
>> also?
>> 
>> In my opinion the first one will be straightforward for users to
>> understand. The later one might result in some confusion.
>> _______________________________________________
>> Mercurial-devel mailing list
>> Mercurial-devel@mercurial-scm.org
>> https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Pulkit Goyal - June 10, 2017, 8:02 a.m.
Sorry for replying late.

>>>
>>> I was working on this and had a doubt. Shall a folder containing files
>>> with only unknown and ignored status can be tersed as unknown?
>>
>> The simple answer to this is yes, if for no other reason than one of the
>> upsides of terse-status is that it doesn't spend forever descending through
>> directory hierarchies that have no files managed by mercurial.
>
> I'm conflicted. In Subversion, the answer would be no, but svn:ignore isn't recursive, and svn tracks directories, so it's not a great comparison. Let's start with following Danek's instinct and see how it feels?

Since we are turning on terse for unknowns by default, the way to opt
out of terse will be bit confusing.

In the above case, it will be like:

$ hg status
? folder/

$ hg status -i
? folder/a.py
I folder/a.pyc

Is there any other way we can turn on the terse for unknown and yield
a better UX.

In my opinion it will be better to have the --terse flag for sometime
and then switch the terse on for default for unknown.
Augie Fackler - June 10, 2017, 1:24 p.m.
> On Jun 10, 2017, at 4:02 AM, Pulkit Goyal <7895pulkit@gmail.com> wrote:
> 
> Sorry for replying late.
> 
>>>> 
>>>> I was working on this and had a doubt. Shall a folder containing files
>>>> with only unknown and ignored status can be tersed as unknown?
>>> 
>>> The simple answer to this is yes, if for no other reason than one of the
>>> upsides of terse-status is that it doesn't spend forever descending through
>>> directory hierarchies that have no files managed by mercurial.
>> 
>> I'm conflicted. In Subversion, the answer would be no, but svn:ignore isn't recursive, and svn tracks directories, so it's not a great comparison. Let's start with following Danek's instinct and see how it feels?
> 
> Since we are turning on terse for unknowns by default, the way to opt
> out of terse will be bit confusing.
> 
> In the above case, it will be like:
> 
> $ hg status
> ? folder/
> 
> $ hg status -i
> ? folder/a.py
> I folder/a.pyc
> 
> Is there any other way we can turn on the terse for unknown and yield
> a better UX.
> 
> In my opinion it will be better to have the --terse flag for sometime
> and then switch the terse on for default for unknown.

Fair enough - but do keep the design with on-by-default at least somewhat in mind.

Patch

diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -4663,6 +4663,7 @@ 
     ('c', 'clean', None, _('show only files without changes')),
     ('u', 'unknown', None, _('show only unknown (not tracked) files')),
     ('i', 'ignored', None, _('show only ignored files')),
+    ('t', 'terse', None, _('Show the tersed status')),
     ('n', 'no-status', None, _('hide status prefix')),
     ('C', 'copies', None, _('show source of copied files')),
     ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
@@ -4780,6 +4781,60 @@ 
     fmt = '%s' + end
     showchar = not opts.get('no_status')
 
+    if opts.get('terse'):
+        #visited = []
+        finalls = [[], [], [], [], [], [], []]
+        for i in range(0,7):
+            reslist = []
+            filedirdict = {}
+            newlist = []
+            fileslist = changestates[i][2]
+            lensuf = len(repo.root + os.path.sep)
+            for file in fileslist:
+                par = repo.root + os.path.sep + os.path.dirname(file)
+                #if par in visited:
+                #    continue
+
+                try:
+                    filedirdict[par].append(file)
+                except KeyError:
+                    filedirdict[par] = [file]
+
+            cpdict = filedirdict.copy()
+            for dirname, fnames in cpdict.iteritems():
+                #visited.append(dirname)
+                # This should be replaced with matcher
+                if len(os.listdir(dirname)) == len(fnames):
+                    dirpar = os.path.dirname(dirname)
+                    try:
+                        filedirdict[dirpar].append(dirname[lensuf:] +
+                                                os.path.sep)
+                    except KeyError:
+                        filedirdict[dirpar] = [dirname[lensuf:]+ os.path.sep]
+                    filedirdict[dirname] = []
+                    if dirname != repo.root:
+                        newlist.append(dirname)
+
+            while(len(newlist) != 0):
+                fname = newlist.pop()
+                par = os.path.dirname(fname)
+
+                if len(os.listdir(par)) == len(filedirdict[par]):
+                    parparent = os.path.dirname(par)
+                    try:
+                        filedirdict[parparent].append(par[lensuf:] + os.path.sep)
+                    except KeyError:
+                        filedirdict[parparent] = [par[lensuf:] + os.path.sep]
+                    filedirdict[par] = []
+                    if parparent != repo.root:
+                        newlist.insert(0, parparent)
+
+            for dirname, fnames in filedirdict.iteritems():
+                finalls[i].extend(fnames)
+
+        changestates = zip(states, pycompat.iterbytestr('MAR!?IC'), finalls)
+
+
     for state, char, files in changestates:
         if state in show:
             label = 'status.' + state
diff --git a/tests/test-terse-status.t b/tests/test-terse-status.t
new file mode 100644
--- /dev/null
+++ b/tests/test-terse-status.t
@@ -0,0 +1,73 @@ 
+  $ hg init repo
+  $ cd repo
+  $ touch a b c
+  $ hg status
+  ? a
+  ? b
+  ? c
+
+This one is not working as its listing .hg as a file, will be fixed when matcher
+will be used
+  $ hg status --terse
+  ? a
+  ? b
+  ? c
+
+  $ mkdir folder
+  $ cd folder
+  $ touch x y z
+  $ hg status
+  ? a
+  ? b
+  ? c
+  ? folder/x
+  ? folder/y
+  ? folder/z
+
+  $ hg status --terse
+  ? folder/
+  ? a
+  ? b
+  ? c
+
+  $ hg add .
+  adding x
+  adding y
+  adding z
+
+  $ hg status
+  A folder/x
+  A folder/y
+  A folder/z
+  ? a
+  ? b
+  ? c
+
+  $ hg status --terse
+  A folder/
+  ? a
+  ? b
+  ? c
+
+  $ mkdir another
+  $ cd another
+  $ touch p q r
+  $ hg status --terse
+  A folder/x
+  A folder/y
+  A folder/z
+  ? folder/another/
+  ? a
+  ? b
+  ? c
+
+  $ hg add .
+  adding p
+  adding q
+  adding r
+
+  $ hg status --terse
+  A folder/
+  ? a
+  ? b
+  ? c