{"thread":{"id":"27389","subject":"[PATCH/WIP] completion: complete git diff with only changed files.","startedAt":"2011-05-18T00:15:03Z","lastAt":"2011-05-19T17:07:18Z","messageCount":6,"participants":["Paul Ebermann","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"168089","messageId":"4DD30F87.2000807@gmx.de","threadId":"27389","inReplyTo":null,"subject":"[PATCH/WIP] completion: complete git diff with only changed files.","fromName":"Paul Ebermann","fromEmail":"paul-ebermann@gmx.de","sentAt":"2011-05-18T00:15:03Z","receivedAt":"2011-05-18T00:15:03Z","isPatch":true,"sender":{"key":"paul-ebermann@gmx.de","avatar":"https://gravatar.com/avatar/cc7e51a73ad3f42554d77240dde18357dc826227b021826a604ed4150e43c6f9?d=mp&s=160"},"body":"The bash completion script for git diff completes either for\nreferences or for file names. If there was a -- given, it uses\nthe default bash filename completion.\n\nThe completion for hg diff completes only changed file names\nhere - this is quite useful if we want to see changes in only\nsome file (or directory). (This was mentioned on stackoverflow,\nwhich gave me the idea: http://stackoverflow.com/q/6034472/600500 )\n\nI ported this idea to git. Now\n   git diff -- <tab>\nwill complete any changed files. It also works for the other ways\nof calling git diff, except the .. and ... notations (as I'm\nnot sure how to do this).\n\nSigned-off-by: Paŭlo Ebermann <Paul-Ebermann@gmx.de>\n---\n\nHello,\nthis is my first contribution to git at all, and I only joined the\nmailing list some hours ago (right before starting to code this), so I hope\nI'm not making any mistakes here. (I read the SubmittingPatches document,\nthough.)\n\n\nI only made this work after the --, while the usual file completion already\nseems to work if there is no --. I'm not really sure what is wanted here.\n\nI'm checking the non-option arguments on being commits (or tags), and pass\nonly the matching ones to the nested `git diff` call.\nIt might be easier to ommit this check and pass everything that does not\nstart with a `-`. Then it would also easily work for the .. and ... syntax,\nI think.\nOpinions?\n\nThe same completion function or a variation might also be useful for other\ncommands like git add (completing changed or new files) or git rm\n(completing already removed files). Input is welcome here.\n\nThis patch is based on the current master branch, I hope this is the right\none.\n\n\n contrib/completion/git-completion.bash |   72 +++++++++++++++++++++++++++++++-\n 1 files changed, 70 insertions(+), 2 deletions(-)\n\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex bb8d7d0..c529bdf 100755\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -663,6 +663,69 @@ __git_compute_merge_strategies ()\n \t: ${__git_merge_strategies:=$(__git_list_merge_strategies)}\n }\n \n+\n+# Completion for the file argument for git diff.\n+# It completes only files actually changed. This might be useful\n+# as completion for other commands as well.\n+#\n+# The idea comes from the bash completion for Mercurial (hg),\n+# which does something similar (but more simple, only difference of\n+# working directory to HEAD and/or index, if I understand right).\n+# It (the idea) was brought to us by the question\n+#      http://stackoverflow.com/q/6034472/600500\n+#  from \"olt\".\n+__git_complete_changed_files()\n+{\n+  #\n+  # We use \"git diff --name-only --relative\" to generate the list,\n+  # but this needs the same --cached and <commit> arguments as the\n+  # command line being constructed.\n+  #\n+\n+\n+    # first grab arguments like --cached and any commit arguments.\n+\n+    local -a args=()\n+    local finish=false\n+\n+    for (( i=1 ; i < cword ; i++)) do\n+    local current_arg=${words[$i]}\n+    #  echo checking $current_arg >&2\n+       case $current_arg in\n+           --cached)\n+               args+=( $current_arg )\n+               ;;\n+           --)\n+               # finish parsing arguments, the rest are file names\n+               break\n+               ;;\n+           -*)\n+               # other options are ignored\n+               ;;\n+           *)\n+               if git cat-file -e $current_arg 2> /dev/null\n+               then\n+                   case $( git cat-file -t $current_arg ) in\n+                       commit|tag)\n+                       # commits and tags are added to the command line.\n+                           args+=( $current_arg )\n+                           # echo adding $current_arg >&2\n+                           ;;\n+                       *)\n+                   esac\n+               fi\n+               ;;\n+       esac\n+    done\n+\n+    # now we can call `git diff`\n+\n+    COMPREPLY=( $( compgen \\\n+        -W \"$( git diff --name-only --relative \"${args[@]}\" -- )\" -- $cur ) )\n+}\n+\n+\n+\n __git_complete_revlist_file ()\n {\n \tlocal pfx ls ref cur_=\"$cur\"\n@@ -1314,10 +1377,14 @@ __git_diff_common_options=\"--stat --numstat --shortstat --summary\n \t\t\t--dirstat-by-file= --cumulative\n \"\n \n+\n _git_diff ()\n {\n-\t__git_has_doubledash && return\n-\n+    if __git_has_doubledash\n+    then\n+        # complete for the file part: only changed files\n+        __git_complete_changed_files\n+    else\n \tcase \"$cur\" in\n \t--*)\n \t\t__gitcomp \"--cached --staged --pickaxe-all --pickaxe-regex\n@@ -1328,6 +1395,7 @@ _git_diff ()\n \t\t;;\n \tesac\n \t__git_complete_revlist_file\n+    fi\n }\n \n __git_mergetools_common=\"diffuse ecmerge emerge kdiff3 meld opendiff\n"},{"id":"168102","messageId":"7v8vu4efvj.fsf@alter.siamese.dyndns.org","threadId":"27389","inReplyTo":"4DD30F87.2000807@gmx.de","subject":"Re: [PATCH/WIP] completion: complete git diff with only changed files.","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-05-18T05:29:04Z","receivedAt":"2011-05-18T05:29:04Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Paul Ebermann <Paul-Ebermann@gmx.de> writes:\n\n> I ported this idea to git. Now\n>    git diff -- <tab>\n> will complete any changed files. It also works for the other ways\n> of calling git diff, except the .. and ... notations (as I'm\n> not sure how to do this).\n\nVery interesting.\n\nI would be a bit dissapointed if this change makes\n\n\tgit diff maint master -- builtin/lo<TAB>\n\nnot complete for me when builtin/log.c did not change between these two\nbranches, as running between different maintenance tracks and the master\nbranch to see how far things have diverged is my first quick sanity check\nbefore deciding where in the history a new patch should be queued.\n\nSpending cycles to keep me waiting while running \"diff\" before giving me\nthe control back, and implicitly telling me by not telling me that lo...\nis a completion candidate, would surely make me go \"Huh? Is it being slow?\nHello? <TAB> <TAAAB> <TAAAAAAAB> ... hmm, did I mistype the directory\nname?  B-U-I-L-T-I-N ... that's correct.... what's going on?  ah wait a\nminute, we recently applied that stupid change that tells me what I really\nwant to know by not telling me, which is backwards.\"\n\nThat's already 12 seconds wasted, and worse yet, even after I learn the\nquirk of the new behaviour that tells what I want to know by not telling\nme, the need to make sure that I didn't mistype the directory name would\nnot disappear.\n\nI would rather not to be retrained to wire my brain backwards in the first\nplace.\n\n> +    local -a args=()\n> +    local finish=false\n> +\n> +    for (( i=1 ; i < cword ; i++)) do\n> +    local current_arg=${words[$i]}\n> +    #  echo checking $current_arg >&2\n> +       case $current_arg in\n> +           --cached)\n\ncase arms align with case and esac in our codebase, i.e.\n\n\tcase $current_arg in\n        --cached)\n        \t...\n                ;;\n                ...\n\tesac\n\n> +           *)\n> +               if git cat-file -e $current_arg 2> /dev/null\n> +               then\n> +                   case $( git cat-file -t $current_arg ) in\n\nI do not see the need for the outer if/then/fi here. Wouldn't this\nsufficient?\n\n\t\tcase \"$(git cat-file -t $current_arg 2>/dev/null)\" in\n\t\tcommit|tag)\n                \t...\n\nIf you are interested in dealing with ../... notation, you could instead\nuse \"git rev-parse --revs-only --no-flags\", e.g.\n\n\t$ git rev-parse --revs-only --no-flags maint..master\n        b602ed7dea968d72c5b3f61ca016de7f285d80ef\n\t^ea1ab4b280ed3b041da53e161e32e38930569f3e\n\n        $ git rev-parse --revs-only --no-flags jch...pu\n        11b715c624d3766546a52cc333bc2ea2e426f631\n        14f92e20522bae26faa841374bbbe6c0d08770de\n        ^14f92e20522bae26faa841374bbbe6c0d08770de\n\nBut I tend to think this change itself is not such a great idea to begin\nwith, so....\n"},{"id":"168135","messageId":"4DD3C814.8000100@gmx.de","threadId":"27389","inReplyTo":"7v8vu4efvj.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH/WIP] completion: complete git diff with only changed files.","fromName":"Paul Ebermann","fromEmail":"paul-ebermann@gmx.de","sentAt":"2011-05-18T13:22:28Z","receivedAt":"2011-05-18T13:22:28Z","isPatch":true,"sender":{"key":"paul-ebermann@gmx.de","avatar":"https://gravatar.com/avatar/cc7e51a73ad3f42554d77240dde18357dc826227b021826a604ed4150e43c6f9?d=mp&s=160"},"body":"Junio C Hamano skribis:\n> Paul Ebermann <Paul-Ebermann@gmx.de> writes:\n> \n>> I ported this idea to git. Now\n>>    git diff -- <tab>\n>> will complete any changed files. It also works for the other ways\n>> of calling git diff, except the .. and ... notations (as I'm\n>> not sure how to do this).\n> \n> Very interesting.\n> \n> I would be a bit dissapointed if this change makes\n> \n> \tgit diff maint master -- builtin/lo<TAB>\n> \n> not complete for me when builtin/log.c did not change between these two\n> branches, as running between different maintenance tracks and the master\n> branch to see how far things have diverged is my first quick sanity check\n> before deciding where in the history a new patch should be queued.\n\nIn fact, it does complete. (Even if there is no maint branch like in my\nclone.) There seems to be a fallback to filename completion if the completion\nlist is empty.  It takes quite longer than before, though.\nAnd it does not give a list of all files if there are changed files starting\nwith the prefix typed.\n\n> Spending cycles to keep me waiting while running \"diff\" before giving me\n> the control back, and implicitly telling me by not telling me that lo...\n> is a completion candidate, would surely make me go \"Huh? Is it being slow?\n> Hello? <TAB> <TAAAB> <TAAAAAAAB> ... hmm, did I mistype the directory\n> name?  B-U-I-L-T-I-N ... that's correct.... what's going on?  ah wait a\n> minute, we recently applied that stupid change that tells me what I really\n> want to know by not telling me, which is backwards.\"\n>\n> That's already 12 seconds wasted, and worse yet, even after I learn the\n> quirk of the new behaviour that tells what I want to know by not telling\n> me, the need to make sure that I didn't mistype the directory name would\n> not disappear.\n>\n> I would rather not to be retrained to wire my brain backwards in the first\n> place.\n\nOkay, this seems to depend on the usage pattern.\n\nI'm normally using (for differences to head) git status  first, and then\nhave a look at the files I really want to see. Then completion of only the\nchanged files seems useful. \n\nI must confess that I seldomly use\n    git diff <commit> <commit> -- <path>\nat all, while it seems to be an important tool for maintainers of projects\nwith lots of contributors (like you).\n\nWhat about having this completion only apply for the no-commit-given case\n(i.e. `git diff -- <path>`), and using the normal file-based completion\nin the case of a given <commit>?\n(But shouldn't we complete for files existent in one of the comparison ends\n instead of in the working tree?)\n\nI also thought about somehow caching the results to make it faster, but the\nproblem with this is that this will change after each commit, and also\nbetween the commits with every edit to a file.\n\n>> +    local -a args=()\n>> +    local finish=false\n>> +\n>> +    for (( i=1 ; i < cword ; i++)) do\n>> +    local current_arg=${words[$i]}\n>> +    #  echo checking $current_arg >&2\n>> +       case $current_arg in\n>> +           --cached)\n> \n> case arms align with case and esac in our codebase, i.e.\n> \n>         case $current_arg in\n>         --cached)\n>         \t...\n>                 ;;\n>                 ...\n>         esac\n\nOkay. (I simply used what my Emacs auto-indented.)\nI will change this if I submit another patch.\n\n>> +           *)\n>> +               if git cat-file -e $current_arg 2> /dev/null\n>> +               then\n>> +                   case $( git cat-file -t $current_arg ) in\n> \n> I do not see the need for the outer if/then/fi here. Wouldn't this\n> sufficient?\n> \n> \t\tcase \"$(git cat-file -t $current_arg 2>/dev/null)\" in\n> \t\tcommit|tag)\n>                 \t...\n\nSeems like it.\nLooks like I was reading the documentation of -e, which said\n\"Suppress all output; instead exit with zero status if <object>\nexists and is a valid object.\", and thus I thought I could avoid\nusing the redirection at all - I then added it when it showed\nthat the redirect still was necessary.\n\nYes, your version works, too.\n\n> If you are interested in dealing with ../... notation, you could instead\n> use \"git rev-parse --revs-only --no-flags\", e.g.\n> \n> \t$ git rev-parse --revs-only --no-flags maint..master\n>         b602ed7dea968d72c5b3f61ca016de7f285d80ef\n> \t^ea1ab4b280ed3b041da53e161e32e38930569f3e\n> \n>         $ git rev-parse --revs-only --no-flags jch...pu\n>         11b715c624d3766546a52cc333bc2ea2e426f631\n>         14f92e20522bae26faa841374bbbe6c0d08770de\n>         ^14f92e20522bae26faa841374bbbe6c0d08770de\n\nThe reason for using cat-file instead of rev-parse was the ability\nto distinguish between commits and blobs (for example). \n\n\t$ git rev-parse --revs-only --no-flags c4d58da4e\n\tc4d58da4e9050c6330ff145914cc379f0600f703\n\n(This is zlib.c in the current master, and the exit code is still 0.\nUsing the git 1.7.1 binaries on OpenSUSE, I did not compile the current\nmaster code.)\n\nI'm not sure this is really necessary, as I wrote before:\n\n>> I'm checking the non-option arguments on being commits (or tags), and pass\n>> only the matching ones to the nested `git diff` call.\n>> It might be easier to ommit this check and pass everything that does not\n>> start with a `-`. Then it would also easily work for the .. and ... syntax,\n>> I think.\n>> Opinions?\n\nThus I could simply write here\n\n\tcase $current_arg in\n\t...\n\t*)\n\t\targs+=( $current_arg )\n\tesac\n\ninstead. If someone supplies something non-commitish, I don't have to care\nabout it. It would also catch any non-option arguments, but it looks like\nthere are none of these (apart from the commits).\n\n> But I tend to think this change itself is not such a great idea to begin\n> with, so....\n\nYeah, I understood this.\n\nIf this is not accepted, I'll publish it separately for the ones who like it\n(not as a patch, but as a separate bash file which you could source after the\nmain comments.)\nStill thanks for your comments.\n\n\nPaŭlo\n"},{"id":"168156","messageId":"7voc2zbwz8.fsf@alter.siamese.dyndns.org","threadId":"27389","inReplyTo":"4DD3C814.8000100@gmx.de","subject":"Re: [PATCH/WIP] completion: complete git diff with only changed files.","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-05-18T20:00:11Z","receivedAt":"2011-05-18T20:00:11Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Paul Ebermann <Paul-Ebermann@gmx.de> writes:\n\n> I'm normally using (for differences to head) git status first, and then\n> have a look at the files I really want to see. Then completion of only\n> the changed files seems useful.\n\nBy the time completion offers you the choices, you already have spent\nenough extra cycles to compute the paths, which is half the cost of\ngenerating the diff itself.\n\nI have this nagging feeling that you are trying to solve a problem that\ndoes not exist.  Perhaps you have too many things going on in your working\ntree at once, and if git helped in such a way that your workflow does not\nhave to touch so many (possibly unrelated) things at once, you do not have\nto worry about unconstrained \"git diff\" output overwhelming you?\n"},{"id":"168213","messageId":"4DD50DA9.8010305@gmx.de","threadId":"27389","inReplyTo":"7voc2zbwz8.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH/WIP] completion: complete git diff with only changed files.","fromName":"Paul Ebermann","fromEmail":"paul-ebermann@gmx.de","sentAt":"2011-05-19T12:31:37Z","receivedAt":"2011-05-19T12:31:37Z","isPatch":true,"sender":{"key":"paul-ebermann@gmx.de","avatar":"https://gravatar.com/avatar/cc7e51a73ad3f42554d77240dde18357dc826227b021826a604ed4150e43c6f9?d=mp&s=160"},"body":"Junio C Hamano schrieb:\n> Paul Ebermann <Paul-Ebermann@gmx.de> writes:\n> \n>> I'm normally using (for differences to head) git status first, and then\n>> have a look at the files I really want to see. Then completion of only\n>> the changed files seems useful.\n> \n> By the time completion offers you the choices, you already have spent\n> enough extra cycles to compute the paths, which is half the cost of\n> generating the diff itself.\n> \n> I have this nagging feeling that you are trying to solve a problem that\n> does not exist.\n\nMaybe.\n\nFor me, it is not so much about saving CPU cycles (I have enough of\nthese) but about not seeing things I don't want to see, and helping me\ndecide what to type. This might be against the Git philosophy, I'm\nstarting to realize.\n\n>  Perhaps you have too many things going on in your working\n> tree at once, and if git helped in such a way that your workflow does not\n> have to touch so many (possibly unrelated) things at once, you do not have\n> to worry about unconstrained \"git diff\" output overwhelming you?\n\nIf I only want to do \"git diff\" (without any paths), I obviously don't\nneed path completion at all.\n\n\nHere is an example:\n\nYesterday, I addes a new Java class (193 lines)\n  src/de/hub/sam/es/managementclient/ssh/TunnelSocketImpl.java\nand at the same time made some changes to\n  src/de/hub/sam/es/managementclient/ssh/ConnectionManager.java\nto use this new class (adding 29 lines).\n\nI wanted to look only at the changes made to ConnectionManager.java.\n\n(The changes to TunnelSocketImpl.java were obvious: creating the whole\nnew class, thus I could look at this in my editor if I wanted).\n\nWith the usual filename-completion, this goes like\n\n    git diff -- s<tab><tab><tab><tab><tab><tab>s<tab>C<tab>\n\nIf I had a broader package tree (like in some other projects),\nit takes even more work, as I must remember which package name\nstarting letters to type between the tabs.\n\n(In some projects I started to choose the package names so that\nthere never would be two sibling directories starting with the\nsame letter, to help autocompletion.)\n\nWith the new completion scheme, this would be\n\n   git diff -- <tab>C<tab>\n\nIt might need doubled time to complete-and-execute, but\nmy computer is quite faster than I can type (and think),\neven if the .git directory is on a NFS.\n\n(You could ask why my shell working directory is not the\n managementclient directory. The reason is that sometimes\n there are files in ./ which get changed, too.)\n\n\nPaul\n"},{"id":"168225","messageId":"7vipt68vqx.fsf@alter.siamese.dyndns.org","threadId":"27389","inReplyTo":"4DD50DA9.8010305@gmx.de","subject":"Re: [PATCH/WIP] completion: complete git diff with only changed files.","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-05-19T17:07:18Z","receivedAt":"2011-05-19T17:07:18Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Paul Ebermann <Paul-Ebermann@gmx.de> writes:\n\n> For me, it is not so much about saving CPU cycles (I have enough of\n> these) but about not seeing things I don't want to see, and helping me\n> decide what to type. This might be against the Git philosophy, I'm\n> starting to realize.\n\nI would say Git UI philosophy is it is justified to spend CPU cycles in\norder to reduce brain cycles (of course it does not justify spending extra\nCPU cycles for no gain), but your change cuts both ways. In the use case I\npresented, it _wasted_ a dozen or so seconds of my brain cycle before I\nget what I wanted to see. In your use case, it will reduce the need to\nwaste your brain cycle skiping the completion you would not want to see to\nget to what you want. So I am not fundamentally opposed to the change, but\nthe trade-off will largely depend on what your workflow is and what system\nyou are on.\n\nOne thing that I am worried about is the latency before getting the list\nof completion. I've heard enough horror stories on a filesystem with slow\nlstat(3) even \"diff-files --name-only\" introduces a noticeable lag, so I\nam not sure limiting this new codepath only to the case where you know the\ncomparison is made between the index and the working tree would save those\nfolks.\n\nThere already are existing knobs in the completion script to tweak how\nmuch extra cycles the user is willing to spend to generate PS1. Perhaps\nthe new codepath can be made to trigger only to people who want it (or the\nother way around, to allow people to disable)?\n"}]}