{"thread":{"id":"42413","subject":"[PATCH 0/2] Better ref summary alignment in \"git fetch\"","startedAt":"2016-05-22T11:20:17Z","lastAt":"2016-07-04T15:53:26Z","messageCount":71,"participants":["Nguyễn Thái Ngọc Duy","Junio C Hamano","Duy Nguyen","Jeff King","Marc Branchaud","Eric Sunshine","Jakub Narębski"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"287202","messageId":"20160522112019.26516-1-pclouds@gmail.com","threadId":"42413","inReplyTo":null,"subject":"[PATCH 0/2] Better ref summary alignment in \"git fetch\"","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-05-22T11:20:17Z","receivedAt":"2016-05-22T11:20:17Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"I recently got a taste of a busy github repository where many branches\nare created (for Pull Requests) every day. Because branch names should\nbe descriptive, branch names could range from 10 to 60 chars long.\nThis makes \"git fetch\" output really messy.\n\nSo I resurrect a patch (1/2) I sent two years ago to keep alignment\nsomewhat better. 2/2 is some more fancy adjustment to make sure one\nreally long branch name will not make the rest of ref list suffer. But\nI think 1/2 is good enough most of the time.\n\nNguyễn Thái Ngọc Duy (2):\n  fetch: better alignment in ref summary\n  fetch: reduce ref column size when there are enough short ref names\n\n builtin/fetch.c | 23 ++++++++++++++++++++++-\n 1 file changed, 22 insertions(+), 1 deletion(-)\n\n-- \n2.8.2.524.g6ff3d78\n"},{"id":"287203","messageId":"20160522112019.26516-2-pclouds@gmail.com","threadId":"42413","inReplyTo":"20160522112019.26516-1-pclouds@gmail.com","subject":"[PATCH 1/2] fetch: better alignment in ref summary","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-05-22T11:20:18Z","receivedAt":"2016-05-22T11:20:18Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Currently fetch hard-codes the \"remote\" column to be 10. For repos\nwith long branch names, the output could look ugly like this\n\nFrom github.com:pclouds/git\n * [new branch]      2nd-index  -> pclouds/2nd-index\n * [new branch]      3nd-index  -> pclouds/3nd-index\n * [new branch]      file-watcher -> pclouds/file-watcher\n * [new branch]      inst       -> pclouds/inst\n * [new branch]      large-file-fixes -> pclouds/large-file-fixes\n * [new branch]      ls         -> pclouds/ls\n * [new branch]      master     -> pclouds/master\n * [new branch]      multiple-work-trees -> pclouds/multiple-work-trees\n * [new branch]      mv         -> pclouds/mv\n * [new branch]      read-cache-daemon -> pclouds/read-cache-daemon\n * [new branch]      split-blob -> pclouds/split-blob\n * [new branch]      split-index -> pclouds/split-index\n * [new branch]      status-fast-fast -> pclouds/status-fast-fast\n * [new branch]      untracked-cache -> pclouds/untracked-cache\n\nThis patch makes the output a bit better with minimum code change\n\nFrom github.com:pclouds/git\n * [new branch]      2nd-index  -> pclouds/2nd-index\n * [new branch]      3nd-index  -> pclouds/3nd-index\n * [new branch]      file-watcher -> pclouds/file-watcher\n * [new branch]      inst         -> pclouds/inst\n * [new branch]      large-file-fixes -> pclouds/large-file-fixes\n * [new branch]      ls               -> pclouds/ls\n * [new branch]      master           -> pclouds/master\n * [new branch]      multiple-work-trees -> pclouds/multiple-work-trees\n * [new branch]      mv                  -> pclouds/mv\n * [new branch]      read-cache-daemon   -> pclouds/read-cache-daemon\n * [new branch]      split-blob          -> pclouds/split-blob\n * [new branch]      split-index         -> pclouds/split-index\n * [new branch]      status-fast-fast    -> pclouds/status-fast-fast\n * [new branch]      untracked-cache     -> pclouds/untracked-cache\n\nTo make all \"->\" aligned, we may need to go through the ref list\ntwice, or buffer the output and let column.c align it. Either way\nneeds a lot more work than this.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/fetch.c | 11 ++++++++++-\n 1 file changed, 10 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 1cf15d0..223e09b 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -465,7 +465,14 @@ fail:\n \t\t\t   : STORE_REF_ERROR_OTHER;\n }\n \n-#define REFCOL_WIDTH  10\n+static int REFCOL_WIDTH = 10;\n+\n+static void adjust_refcol_width(int len)\n+{\n+\tif (REFCOL_WIDTH < len) {\n+\t\tREFCOL_WIDTH = len;\n+\t}\n+}\n \n static int update_local_ref(struct ref *ref,\n \t\t\t    const char *remote,\n@@ -477,6 +484,8 @@ static int update_local_ref(struct ref *ref,\n \tstruct branch *current_branch = branch_get(NULL);\n \tconst char *pretty_ref = prettify_refname(ref->name);\n \n+\tadjust_refcol_width(gettext_width(remote));\n+\n \ttype = sha1_object_info(ref->new_oid.hash, NULL);\n \tif (type < 0)\n \t\tdie(_(\"object %s not found\"), oid_to_hex(&ref->new_oid));\n-- \n2.8.2.524.g6ff3d78\n"},{"id":"287204","messageId":"20160522112019.26516-3-pclouds@gmail.com","threadId":"42413","inReplyTo":"20160522112019.26516-1-pclouds@gmail.com","subject":"[PATCH 2/2] fetch: reduce ref column size when there are enough short ref names","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-05-22T11:20:19Z","receivedAt":"2016-05-22T11:20:19Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"A flaw with the previous patch is, if there is a single long ref name,\nthe rest of the ref list will be aligned with the big column, wasting\nlots of space (and potentially be wrapped around by the terminal, making\nit even harder to read).\n\nThis patch tries to mitigate that. If there are five consecutive refs\nwhose name is at least ten chars shorter than column width, the width is\nreduced by ten. Five and ten are picked out of thin air. But the short\nwidth reduction (instead of \"REFCOL_WIDTH = len\") is to avoid the column\nwidth being reduced then grown back too much and too fast (imagine the\nnext ref is really short then the ref after that is super long).\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/fetch.c | 12 ++++++++++++\n 1 file changed, 12 insertions(+)\n\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 223e09b..ae2ff0c 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -469,8 +469,20 @@ static int REFCOL_WIDTH = 10;\n \n static void adjust_refcol_width(int len)\n {\n+\tstatic int reduce_width_count;\n+\n+\tif (REFCOL_WIDTH > 10 && len < REFCOL_WIDTH - 10) {\n+\t\treduce_width_count++;\n+\t\tif (reduce_width_count == 5) {\n+\t\t\tREFCOL_WIDTH -= 10;\n+\t\t\treduce_width_count = 0;\n+\t\t}\n+\t} else\n+\t\treduce_width_count = 0;\n+\n \tif (REFCOL_WIDTH < len) {\n \t\tREFCOL_WIDTH = len;\n+\t\treduce_width_count = 0;\n \t}\n }\n \n-- \n2.8.2.524.g6ff3d78\n"},{"id":"287216","messageId":"xmqqfut9bnff.fsf@gitster.mtv.corp.google.com","threadId":"42413","inReplyTo":"20160522112019.26516-2-pclouds@gmail.com","subject":"Re: [PATCH 1/2] fetch: better alignment in ref summary","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-05-23T00:58:12Z","receivedAt":"2016-05-23T00:58:12Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> Currently fetch hard-codes the \"remote\" column to be 10. For repos\n> with long branch names, the output could look ugly like this\n>\n> From github.com:pclouds/git\n>  * [new branch]      2nd-index  -> pclouds/2nd-index\n>  * [new branch]      3nd-index  -> pclouds/3nd-index\n>  * [new branch]      file-watcher -> pclouds/file-watcher\n>  ...\n\nSo, we have to show \"an object taken from their name is copied to\nour name\", and somebody designed the format to report it to use\n\"their-name -> our-name\", and decided that a certain number of\ncolumns is sufficient for \"their-name\" part and that attempting to\nalign \"->\" sign is a good idea..\n\nThat was long before a few best practices were established.  We\nencourage people to use longer and more descriptive branch names, so\n\"their-name\" is a lot longer than 10 columns, which contradicts the\nfirst one of two old assumptions.  And we want to keep the second\nold assumption alive.\n\nProgressively pushing \"->\" to the right like you did might be a\ncheap way to do so, but shouldn't we be also taking advantage of\nanother best practice that has been established since we started\nreporting this \"their-name came to our-name\", namely, very often,\nour-name is a short and fixed prefix plus their-name?\n\nThat is, I wonder if the above can become something like:\n\n> From github.com:pclouds/git\n>  * [new branch]      { -> pclouds/}2nd-index\n>  * [new branch]      { -> pclouds/}3nd-index\n>  * [new branch]      { -> pclouds/}file-watcher\n>  ...\n\nThe above example borrows the idea used in diffstat label for\nrenaming patch and I think you can design a better notataion, but a\nbig point is that you can shorten the whole thing by not repeating\nthe common part twice.  The arrow aligns merely as a side effect of\nthe shortening, taking advantage of the fact that most people fetch\nwith \"$their_prefix/*:$our_prefix/*\" renaming refspec.\n"},{"id":"287222","messageId":"CACsJy8BS2n8T1smxEJMPNiXBvMYHWnQ9BQGk_OKqJd1GFcuk7w@mail.gmail.com","threadId":"42413","inReplyTo":"xmqqfut9bnff.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH 1/2] fetch: better alignment in ref summary","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2016-05-23T01:59:59Z","receivedAt":"2016-05-23T01:59:59Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Mon, May 23, 2016 at 7:58 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> That is, I wonder if the above can become something like:\n>\n>> From github.com:pclouds/git\n>>  * [new branch]      { -> pclouds/}2nd-index\n>>  * [new branch]      { -> pclouds/}3nd-index\n>>  * [new branch]      { -> pclouds/}file-watcher\n>>  ...\n>\n> The above example borrows the idea used in diffstat label for\n> renaming patch and I think you can design a better notataion, but a\n> big point is that you can shorten the whole thing by not repeating\n> the common part twice.  The arrow aligns merely as a side effect of\n> the shortening, taking advantage of the fact that most people fetch\n> with \"$their_prefix/*:$our_prefix/*\" renaming refspec.\n\nI did think of that. My only concern is, with the new output I can't\nsimply copy the ref name (e.g. pclouds/2nd-index) and use it to, say,\nexamine with git-log. But I'm more of a \"tab tab tab\" person than\n\"copy and paste\", so I don't know how often people need to do that.\n-- \nDuy\n"},{"id":"287553","messageId":"20160526051820.GE6756@sigill.intra.peff.net","threadId":"42413","inReplyTo":"20160522112019.26516-2-pclouds@gmail.com","subject":"Re: [PATCH 1/2] fetch: better alignment in ref summary","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2016-05-26T05:18:21Z","receivedAt":"2016-05-26T05:18:21Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Sun, May 22, 2016 at 06:20:18PM +0700, Nguyễn Thái Ngọc Duy wrote:\n\n> To make all \"->\" aligned, we may need to go through the ref list\n> twice, or buffer the output and let column.c align it. Either way\n> needs a lot more work than this.\n\nI don't think a two-pass approach is _too_ bad. The trickiest thing is\nthat we handle the \"prune\" refs separately, even though they go in the\nsame status table.\n\nHowever, I tried it, and the results looked much worse for my example\nrepo than yours. The problem is that I had one gigantic refname, and\nthat shoved the \"->\" and everything after far to the right, where they\nwrapped to the next line.\n\nThough the stair-stepping in your patch is funny, the output is easier\nto read.\n\nI do agree with Junio that we could probably improve the output quite a\nbit by not showing each refname twice in the common case. I don't quite\nfind the \"{ -> origin/}whatever\" particularly pretty, but something like\nthat which keeps the variable bits at the end would make this problem\njust go away.\n\n> -#define REFCOL_WIDTH  10\n> +static int REFCOL_WIDTH = 10;\n\nThis should probably go lower-case if it's not a preprocessor macro\nanymore. I know it makes the diff a lot noisier, but I think it's worth\nit.\n\n-Peff\n"},{"id":"287577","messageId":"574706A1.8040606@xiplink.com","threadId":"42413","inReplyTo":"CACsJy8BS2n8T1smxEJMPNiXBvMYHWnQ9BQGk_OKqJd1GFcuk7w@mail.gmail.com","subject":"Re: [PATCH 1/2] fetch: better alignment in ref summary","fromName":"Marc Branchaud","fromEmail":"marcnarc@xiplink.com","sentAt":"2016-05-26T14:22:25Z","receivedAt":"2016-05-26T14:22:25Z","isPatch":true,"sender":{"key":"marcnarc@xiplink.com","avatar":"https://avatars.githubusercontent.com/u/14980203?v=4"},"body":"On 2016-05-22 09:59 PM, Duy Nguyen wrote:\n> On Mon, May 23, 2016 at 7:58 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>> That is, I wonder if the above can become something like:\n>>\n>>>  From github.com:pclouds/git\n>>>   * [new branch]      { -> pclouds/}2nd-index\n>>>   * [new branch]      { -> pclouds/}3nd-index\n>>>   * [new branch]      { -> pclouds/}file-watcher\n>>>   ...\n>>\n>> The above example borrows the idea used in diffstat label for\n>> renaming patch and I think you can design a better notataion, but a\n>> big point is that you can shorten the whole thing by not repeating\n>> the common part twice.  The arrow aligns merely as a side effect of\n>> the shortening, taking advantage of the fact that most people fetch\n>> with \"$their_prefix/*:$our_prefix/*\" renaming refspec.\n>\n> I did think of that. My only concern is, with the new output I can't\n> simply copy the ref name (e.g. pclouds/2nd-index) and use it to, say,\n> examine with git-log. But I'm more of a \"tab tab tab\" person than\n> \"copy and paste\", so I don't know how often people need to do that.\n\nWhy do we need any kind of \"->\" at all?  How about simply (with an \nupdate to \"old-branch\" for comparison to probably-more-common output):\n\n From github.com:pclouds/git\n    cafed0c..badfeed  pclouds/old-branch\n  * [new branch]      pclouds/2nd-index\n  * [new branch]      pclouds/3nd-index\n  * [new branch]      pclouds/file-watcher\n\n\t\tM.\n"},{"id":"287584","messageId":"20160526162940.GB18210@sigill.intra.peff.net","threadId":"42413","inReplyTo":"574706A1.8040606@xiplink.com","subject":"Re: [PATCH 1/2] fetch: better alignment in ref summary","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2016-05-26T16:29:40Z","receivedAt":"2016-05-26T16:29:40Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, May 26, 2016 at 10:22:25AM -0400, Marc Branchaud wrote:\n\n> Why do we need any kind of \"->\" at all?  How about simply (with an update to\n> \"old-branch\" for comparison to probably-more-common output):\n> \n> From github.com:pclouds/git\n>    cafed0c..badfeed  pclouds/old-branch\n>  * [new branch]      pclouds/2nd-index\n>  * [new branch]      pclouds/3nd-index\n>  * [new branch]      pclouds/file-watcher\n\nThat covers the common case of:\n\n  refs/heads/*:refs/remotes/pclouds/*\n\nbut sometimes the remote and local names are not the same, and the\nmapping is interesting. Like:\n\n  $ git fetch origin refs/pull/*/head:refs/remotes/pr/*\n  ...\n   * [new ref]         refs/pull/77/head -> pr/77\n\nOr even:\n\n  $ git fetch origin refs/pull/77/head refs/pull/78/head\n  From ...\n   * branch            refs/pull/77/head -> FETCH_HEAD\n   * branch            refs/pull/78/head -> FETCH_HEAD\n\nSo I think we need a scheme that can show the interesting mappings, but\ncollapses to something simple for the common case.\n\n-Peff\n"},{"id":"287601","messageId":"xmqq8tywwwab.fsf@gitster.mtv.corp.google.com","threadId":"42413","inReplyTo":"20160526162940.GB18210@sigill.intra.peff.net","subject":"Re: [PATCH 1/2] fetch: better alignment in ref summary","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-05-26T17:42:36Z","receivedAt":"2016-05-26T17:42:36Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> On Thu, May 26, 2016 at 10:22:25AM -0400, Marc Branchaud wrote:\n>\n>> Why do we need any kind of \"->\" at all?  How about simply (with an update to\n>> \"old-branch\" for comparison to probably-more-common output):\n>> \n>> From github.com:pclouds/git\n>>    cafed0c..badfeed  pclouds/old-branch\n>>  * [new branch]      pclouds/2nd-index\n>>  * [new branch]      pclouds/3nd-index\n>>  * [new branch]      pclouds/file-watcher\n>\n> That covers the common case of:\n>\n>   refs/heads/*:refs/remotes/pclouds/*\n>\n> but sometimes the remote and local names are not the same, and the\n> mapping is interesting. Like:\n>\n>   $ git fetch origin refs/pull/*/head:refs/remotes/pr/*\n>   ...\n>    * [new ref]         refs/pull/77/head -> pr/77\n>\n> Or even:\n>\n>   $ git fetch origin refs/pull/77/head refs/pull/78/head\n>   From ...\n>    * branch            refs/pull/77/head -> FETCH_HEAD\n>    * branch            refs/pull/78/head -> FETCH_HEAD\n>\n> So I think we need a scheme that can show the interesting mappings, but\n> collapses to something simple for the common case.\n\nTrue.  One of the entries in Marc's example is easily misread as\n\"pclouds/2nd-index branch at its refs/heads/pclouds/2nd-index was\nfetched to its usual place\", when Marc wanted to say \"they had\n2nd-index branch at refs/heads/2nd-index, and it was copied to our\nrefs/remotes/pclouds/2nd-index\".\n\nSo even though we might be able to make sure it is unambiguous\nwithout \"this -> that\" (\"->\" is pronounced as 'became'), it is\neasily misread.\n"},{"id":"287603","messageId":"57473CDA.5000408@xiplink.com","threadId":"42413","inReplyTo":"xmqq8tywwwab.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH 1/2] fetch: better alignment in ref summary","fromName":"Marc Branchaud","fromEmail":"marcnarc@xiplink.com","sentAt":"2016-05-26T18:13:46Z","receivedAt":"2016-05-26T18:13:46Z","isPatch":true,"sender":{"key":"marcnarc@xiplink.com","avatar":"https://avatars.githubusercontent.com/u/14980203?v=4"},"body":"On 2016-05-26 01:42 PM, Junio C Hamano wrote:\n>\n> True.  One of the entries in Marc's example is easily misread as\n> \"pclouds/2nd-index branch at its refs/heads/pclouds/2nd-index was\n> fetched to its usual place\", when Marc wanted to say \"they had\n> 2nd-index branch at refs/heads/2nd-index, and it was copied to our\n> refs/remotes/pclouds/2nd-index\".\n>\n> So even though we might be able to make sure it is unambiguous\n> without \"this -> that\" (\"->\" is pronounced as 'became'), it is\n> easily misread.\n\nActually, I tend to just think of it as \"this is a local name you can \nuse to refer to the new thing that was just fetched.\"  The left-hand \nside describes the thing being fetched (new or updated branch/tag/ref), \nand the right hand side shows how to locally refer to that thing.\n\nThe fact that something is buried in some odd part of the ref tree is \nless relevant, IMO.  If I'm using custom fetch refspecs or other \noddities, I'll have that in the back of my head.  But what I really care \nabout is what ref I can use with commands like log and checkout.\n\nSo, regarding Peff's examples:\n\n > $ git fetch origin refs/pull/*/head:refs/remotes/pr/*\n\nJust show me the \"pr/foo\" refs.  I know where things are coming from. \nEven if I configured that fetch refspec a long time ago, I don't need to \nsee the exact mapping every time I fetch.\n\n > $ git fetch origin refs/pull/77/head refs/pull/78/head\n\nAh, now this is an odd case.  Maybe there should be a different output \nformat altogether for this one.  The problem is that the remote refs \nbeing fetched are stored without any kind of local ref.  (Commands like \n\"git log FETCH_HEAD\" only work with the last ref fetched, even though \nall the SHAs get added to the .git/FETCH_HEAD file.  Maybe if git \nsupported a \"FETCH_HEAD@{x}\" syntax...)\n\nI think the output should show the plain SHA values, since those are the \nonly things the user can use to work with those refs.  Maybe something like:\n\n\tFrom ...\n\t * origin:refs/pull/77/head  abcd0123\n\t * origin:refs/pull/78/head  453def00\n\n(Not 100% sure about the \"origin:\" prefix...)\n\n\t\tM.\n"},{"id":"287608","messageId":"xmqqk2igvcoj.fsf@gitster.mtv.corp.google.com","threadId":"42413","inReplyTo":"57473CDA.5000408@xiplink.com","subject":"Re: [PATCH 1/2] fetch: better alignment in ref summary","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-05-26T19:31:24Z","receivedAt":"2016-05-26T19:31:24Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Marc Branchaud <marcnarc@xiplink.com> writes:\n\n> The fact that something is buried in some odd part of the ref tree is\n> less relevant, IMO.  If I'm using custom fetch refspecs or other\n> oddities, I'll have that in the back of my head.  But what I really\n> care about is what ref I can use with commands like log and checkout.\n>\n> So, regarding Peff's examples:\n>\n>> $ git fetch origin refs/pull/*/head:refs/remotes/pr/*\n>\n> Just show me the \"pr/foo\" refs.  I know where things are coming\n> from. Even if I configured that fetch refspec a long time ago, I don't\n> need to see the exact mapping every time I fetch.\n\nThat is only because you are used to seeing them.  You cannot claim\n\"I'll remember forever without seeing them\" without actually\nexperiencing not seeing them for a long time.\n\n> I think the output should show the plain SHA values, since those are\n> the only things the user can use to work with those refs.\n\nWhen they tell others how to reproduce what they did (or record what\nthey did so that they can reproduce it later), they need what it is\ncalled at the remote end.\n\nI would hesitate to go in the approach based on discarding\ninformation, as if it is the only way to shorten and clarify the\noutput.  Let's not do so before thinking things through to achieve\nthe same while keeping the information we give to the users.\n"},{"id":"287620","messageId":"57477514.9020800@xiplink.com","threadId":"42413","inReplyTo":"xmqqk2igvcoj.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH 1/2] fetch: better alignment in ref summary","fromName":"Marc Branchaud","fromEmail":"marcnarc@xiplink.com","sentAt":"2016-05-26T22:13:40Z","receivedAt":"2016-05-26T22:13:40Z","isPatch":true,"sender":{"key":"marcnarc@xiplink.com","avatar":"https://avatars.githubusercontent.com/u/14980203?v=4"},"body":"On 2016-05-26 03:31 PM, Junio C Hamano wrote:\n> Marc Branchaud <marcnarc@xiplink.com> writes:\n>\n>> The fact that something is buried in some odd part of the ref tree is\n>> less relevant, IMO.  If I'm using custom fetch refspecs or other\n>> oddities, I'll have that in the back of my head.  But what I really\n>> care about is what ref I can use with commands like log and checkout.\n>>\n>> So, regarding Peff's examples:\n>>\n>>> $ git fetch origin refs/pull/*/head:refs/remotes/pr/*\n>>\n>> Just show me the \"pr/foo\" refs.  I know where things are coming\n>> from. Even if I configured that fetch refspec a long time ago, I don't\n>> need to see the exact mapping every time I fetch.\n>\n> That is only because you are used to seeing them.  You cannot claim\n> \"I'll remember forever without seeing them\" without actually\n> experiencing not seeing them for a long time.\n\nI don't expect (or even want) to forever remember the mapping.  It's a \nmatter of context.\n\nWhen fetching, I'm interested in shiny new refs and I want to work with \nthem.\n\nWhen configuring a remote repository, I'm interested in how to bring \nthat repo's refs into my own repository.\n\nThese are two distinct modes of thought, and I don't think fetch's \noutput always needs to display the latter.  Perhaps the --verbose switch \ncould turn on showing how the remote refs get mapped.  I can see how \nthat would occasionally be useful for debugging fetch refspecs.\n\n>> I think the output should show the plain SHA values, since those are\n>> the only things the user can use to work with those refs.\n>\n> When they tell others how to reproduce what they did (or record what\n> they did so that they can reproduce it later), they need what it is\n> called at the remote end.\n\nWhich is why I included the refnames in my proposal to Peff's second \nexample.  Really, the SHA and the remote's name for the SHA are the only \nmeaningful things in this case.\n\n> I would hesitate to go in the approach based on discarding\n> information, as if it is the only way to shorten and clarify the\n> output.  Let's not do so before thinking things through to achieve\n> the same while keeping the information we give to the users.\n\nI agree, this is not something to undertake lightly.  But I've yet to be \nconvinced of the utility of always showing the ref mapping during a fetch.\n\nI actually found fetch's output quite confusing when I first started \nusing git.  It's really not obvious that the thing on the left of the \n\"->\" is the remote's local name, especially since to see what was \nfetched one has to use the thing on the right-hand side -- which is \nobviously in a remote-specific namespace.  (Admittedly, this was before \ncheckout learned to search refs/remotes/ for things to check out.)\n\n\t\tM.\n"},{"id":"288119","messageId":"CACsJy8B1PXhSNSu-p5vdk7crHsy7cKisxJ0rtZXsknrZorx+Ug@mail.gmail.com","threadId":"42413","inReplyTo":"20160526051820.GE6756@sigill.intra.peff.net","subject":"Re: [PATCH 1/2] fetch: better alignment in ref summary","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2016-06-02T13:58:27Z","receivedAt":"2016-06-02T13:58:27Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Mon, May 23, 2016 at 7:58 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> That is, I wonder if the above can become something like:\n>\n>> From github.com:pclouds/git\n>>  * [new branch]      { -> pclouds/}2nd-index\n>>  * [new branch]      { -> pclouds/}3nd-index\n>>  * [new branch]      { -> pclouds/}file-watcher\n>>  ...\n\nfor context of the following quote...\n\nOn Thu, May 26, 2016 at 12:18 PM, Jeff King <peff@peff.net> wrote:\n> I do agree with Junio that we could probably improve the output quite a\n> bit by not showing each refname twice in the common case. I don't quite\n> find the \"{ -> origin/}whatever\" particularly pretty, but something like\n> that which keeps the variable bits at the end would make this problem\n> just go away.\n\nI tried it out. It's a bit hard to read at the \"/}\" boundary. Color\nhighlight might help. But it occurs to me, could we extend refspec a\nbit to allow \"foo/bar:base/...\" be be equivalent of\n\"foo/bar:base/foo/bar\". Then fetch output could become\n\n>>  * [new branch]      2nd-index:pclouds/...\n>>  * [new branch]      3nd-index:pclouds/...\n>>  * [new branch]      file-watcher:pclouds/...\n\nIt might be a tiny bit better, and a forced update could be displayed\nwith a prefix '+'. Hmm?\n-- \nDuy\n"},{"id":"288133","messageId":"xmqqk2i7va53.fsf@gitster.mtv.corp.google.com","threadId":"42413","inReplyTo":"CACsJy8B1PXhSNSu-p5vdk7crHsy7cKisxJ0rtZXsknrZorx+Ug@mail.gmail.com","subject":"Re: [PATCH 1/2] fetch: better alignment in ref summary","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-06-02T16:16:40Z","receivedAt":"2016-06-02T16:16:40Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Duy Nguyen <pclouds@gmail.com> writes:\n\n> On Mon, May 23, 2016 at 7:58 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>> That is, I wonder if the above can become something like:\n>>\n>>> From github.com:pclouds/git\n>>>  * [new branch]      { -> pclouds/}2nd-index\n>>>  * [new branch]      { -> pclouds/}3nd-index\n>>>  * [new branch]      { -> pclouds/}file-watcher\n>>>  ...\n>\n> for context of the following quote...\n>\n> On Thu, May 26, 2016 at 12:18 PM, Jeff King <peff@peff.net> wrote:\n>> I do agree with Junio that we could probably improve the output quite a\n>> bit by not showing each refname twice in the common case. I don't quite\n>> find the \"{ -> origin/}whatever\" particularly pretty, but something like\n>> that which keeps the variable bits at the end would make this problem\n>> just go away.\n>\n> I tried it out. It's a bit hard to read at the \"/}\" boundary. Color\n> highlight might help. But it occurs to me, could we extend refspec a\n> bit to allow \"foo/bar:base/...\" be be equivalent of\n> \"foo/bar:base/foo/bar\". Then fetch output could become\n>\n>>>  * [new branch]      2nd-index:pclouds/...\n>>>  * [new branch]      3nd-index:pclouds/...\n>>>  * [new branch]      file-watcher:pclouds/...\n>\n> It might be a tiny bit better, and a forced update could be displayed\n> with a prefix '+'. Hmm?\n\nI do not find that \"...\" particularly more readable, but that\nprobably is very subjective.  It is much less copy&pastable, when\ncompared to pasting \"pclouds/}2nd-index\" and then removing \"}\".\n"},{"id":"288218","messageId":"20160603110843.15434-1-pclouds@gmail.com","threadId":"42413","inReplyTo":"20160522112019.26516-1-pclouds@gmail.com","subject":"[PATCH v2 0/3] Better ref summary alignment in \"git fetch\"","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-06-03T11:08:40Z","receivedAt":"2016-06-03T11:08:40Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"v2 reformats \"abc/common -> def/common\" to \"{abc -> def}/common\"\ninstead and fall back to \"a -> b\" when they have nothing in commmon\n(e.g. \"HEAD -> FETCH_HEAD\"). We could add an option if a user wants to\nstick with \"a -> b\" (and we could do some alignment there as well) but\nit's not part of this series.\n\nIt's a shame that the flag '-' in these ref update lines is not the\nsame in fetch and push (see 1/3). Because git-fetch does not support\n--porcelain option, maybe it's not too late to change its meaning...  \n\nNguyễn Thái Ngọc Duy (3):\n  git-fetch.txt: document fetch output\n  fetch: refactor ref update status formatting code\n  fetch: reduce duplicate in ref update status lines\n\n Documentation/git-fetch.txt |  53 +++++++++++++++++++++\n builtin/fetch.c             | 112 +++++++++++++++++++++++++++++---------------\n t/t5510-fetch.sh            |   4 +-\n t/t5526-fetch-submodules.sh |  26 +++++-----\n 4 files changed, 141 insertions(+), 54 deletions(-)\n\n-- \n2.8.2.524.g6ff3d78\n"},{"id":"288219","messageId":"20160603110843.15434-2-pclouds@gmail.com","threadId":"42413","inReplyTo":"20160603110843.15434-1-pclouds@gmail.com","subject":"[PATCH v2 1/3] git-fetch.txt: document fetch output","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-06-03T11:08:41Z","receivedAt":"2016-06-03T11:08:41Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This documents the ref update status of fetch. The structure of this\noutput is defined in [1]. The ouput content is refined a bit in [2]\n[3] [4].\n\nThis patch is a copy from git-push.txt, modified a bit because the\nflag '-' means different things in push (delete) and fetch (tag\nupdate). We probably should unify the documents at some point in\nfuture.\n\nPS. For code archaeologists, the discussion mentioned in [1] is\nprobably [5].\n\n[1] 165f390 (git-fetch: more terse fetch output - 2007-11-03)\n[2] 6315472 (fetch: report local storage errors ... - 2008-06-26)\n[3] f360d84 (builtin-fetch: add --prune option - 2009-11-10)\n[4] 0997ada (fetch: describe new refs based on where... - 2012-04-16)\n[5] http://thread.gmane.org/gmane.comp.version-control.git/61657\n---\n Documentation/git-fetch.txt | 46 +++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 46 insertions(+)\n\ndiff --git a/Documentation/git-fetch.txt b/Documentation/git-fetch.txt\nindex efe56e0..18e733c 100644\n--- a/Documentation/git-fetch.txt\n+++ b/Documentation/git-fetch.txt\n@@ -99,6 +99,52 @@ The latter use of the `remote.<repository>.fetch` values can be\n overridden by giving the `--refmap=<refspec>` parameter(s) on the\n command line.\n \n+OUTPUT\n+------\n+\n+The output of \"git fetch\" depends on the transport method used; this\n+section describes the output when pushing over the Git protocol\n+(either locally or via ssh).\n+\n+The status of the push is output in tabular form, with each line\n+representing the status of a single ref. Each line is of the form:\n+\n+-------------------------------\n+ <flag> <summary> <from> -> <to> (<reason>)\n+-------------------------------\n+\n+The status of up-to-date refs is shown only if --verbose option is\n+used.\n+\n+flag::\n+\tA single character indicating the status of the ref:\n+(space);; for a successfully fetched fast-forward;\n+`+`;; for a successful forced update;\n+`x`;; for a successfully deleted ref;\n+`-`;; for a successful tag update;\n+`*`;; for a successfully fetched new ref;\n+`!`;; for a ref that was rejected or failed to update; and\n+`=`;; for a ref that was up to date and did not need fetching.\n+\n+summary::\n+\tFor a successfully fetched ref, the summary shows the old and new\n+\tvalues of the ref in a form suitable for using as an argument to\n+\t`git log` (this is `<old>..<new>` in most cases, and\n+\t`<old>...<new>` for forced non-fast-forward updates).\n+\n+from::\n+\tThe name of the remote ref being fetched from, minus its\n+\t`refs/<type>/` prefix. In the case of deletion, the name of\n+\tthe remote ref is \"(none)\".\n+\n+to::\n+\tThe name of the local ref being updated, minus its\n+\t`refs/<type>/` prefix.\n+\n+reason::\n+\tA human-readable explanation. In the case of successfully fetched\n+\trefs, no explanation is needed. For a failed ref, the reason for\n+\tfailure is described.\n \n EXAMPLES\n --------\n-- \n2.8.2.524.g6ff3d78\n"},{"id":"288221","messageId":"20160603110843.15434-3-pclouds@gmail.com","threadId":"42413","inReplyTo":"20160603110843.15434-1-pclouds@gmail.com","subject":"[PATCH v2 2/3] fetch: refactor ref update status formatting code","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-06-03T11:08:42Z","receivedAt":"2016-06-03T11:08:42Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This makes it easier to change the formatting later. And it makes sure\ntranslators cannot mess up format specifiers and break Git.\n\nThere are a couple call sites where the length of the second column is\nTRANSPORT_SUMMARY_WIDTH instead of calculated by TRANSPORT_SUMMARY(),\nwhich is enforced now. The result should be the same because these call\nsites do not contain characters outside ASCII range.\n\nThe two strbuf_addf() calls instead of one is mostly to reduce\ndiff-noise in a future patch where \"ref -> ref\" is reformatted\ndifferently.\n---\n builtin/fetch.c | 77 ++++++++++++++++++++++++++++-----------------------------\n 1 file changed, 38 insertions(+), 39 deletions(-)\n\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 1582ca7..a7f152a 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -451,6 +451,16 @@ fail:\n \n #define REFCOL_WIDTH  10\n \n+static void format_display(struct strbuf *display, char code,\n+\t\t\t   const char *summary, const char *error,\n+\t\t\t   const char *remote, const char *local)\n+{\n+\tstrbuf_addf(display, \"%c %-*s \", code, TRANSPORT_SUMMARY(summary));\n+\tstrbuf_addf(display, \"%-*s -> %s\", REFCOL_WIDTH, remote, local);\n+\tif (error)\n+\t\tstrbuf_addf(display, \"  (%s)\", error);\n+}\n+\n static int update_local_ref(struct ref *ref,\n \t\t\t    const char *remote,\n \t\t\t    const struct ref *remote_ref,\n@@ -467,9 +477,8 @@ static int update_local_ref(struct ref *ref,\n \n \tif (!oidcmp(&ref->old_oid, &ref->new_oid)) {\n \t\tif (verbosity > 0)\n-\t\t\tstrbuf_addf(display, \"= %-*s %-*s -> %s\",\n-\t\t\t\t    TRANSPORT_SUMMARY(_(\"[up to date]\")),\n-\t\t\t\t    REFCOL_WIDTH, remote, pretty_ref);\n+\t\t\tformat_display(display, '=', _(\"[up to date]\"), NULL,\n+\t\t\t\t       remote, pretty_ref);\n \t\treturn 0;\n \t}\n \n@@ -481,10 +490,9 @@ static int update_local_ref(struct ref *ref,\n \t\t * If this is the head, and it's not okay to update\n \t\t * the head, and the old value of the head isn't empty...\n \t\t */\n-\t\tstrbuf_addf(display,\n-\t\t\t    _(\"! %-*s %-*s -> %s  (can't fetch in current branch)\"),\n-\t\t\t    TRANSPORT_SUMMARY(_(\"[rejected]\")),\n-\t\t\t    REFCOL_WIDTH, remote, pretty_ref);\n+\t\tformat_display(display, '!', _(\"[rejected]\"),\n+\t\t\t       _(\"can't fetch in current branch\"),\n+\t\t\t       remote, pretty_ref);\n \t\treturn 1;\n \t}\n \n@@ -492,11 +500,9 @@ static int update_local_ref(struct ref *ref,\n \t    starts_with(ref->name, \"refs/tags/\")) {\n \t\tint r;\n \t\tr = s_update_ref(\"updating tag\", ref, 0);\n-\t\tstrbuf_addf(display, \"%c %-*s %-*s -> %s%s\",\n-\t\t\t    r ? '!' : '-',\n-\t\t\t    TRANSPORT_SUMMARY(_(\"[tag update]\")),\n-\t\t\t    REFCOL_WIDTH, remote, pretty_ref,\n-\t\t\t    r ? _(\"  (unable to update local ref)\") : \"\");\n+\t\tformat_display(display, r ? '!' : '-', _(\"[tag update]\"),\n+\t\t\t       r ? _(\"unable to update local ref\") : NULL,\n+\t\t\t       remote, pretty_ref);\n \t\treturn r;\n \t}\n \n@@ -527,11 +533,9 @@ static int update_local_ref(struct ref *ref,\n \t\t    (recurse_submodules != RECURSE_SUBMODULES_ON))\n \t\t\tcheck_for_new_submodule_commits(ref->new_oid.hash);\n \t\tr = s_update_ref(msg, ref, 0);\n-\t\tstrbuf_addf(display, \"%c %-*s %-*s -> %s%s\",\n-\t\t\t    r ? '!' : '*',\n-\t\t\t    TRANSPORT_SUMMARY(what),\n-\t\t\t    REFCOL_WIDTH, remote, pretty_ref,\n-\t\t\t    r ? _(\"  (unable to update local ref)\") : \"\");\n+\t\tformat_display(display, r ? '!' : '*', what,\n+\t\t\t       r ? _(\"unable to update local ref\") : NULL,\n+\t\t\t       remote, pretty_ref);\n \t\treturn r;\n \t}\n \n@@ -545,11 +549,9 @@ static int update_local_ref(struct ref *ref,\n \t\t    (recurse_submodules != RECURSE_SUBMODULES_ON))\n \t\t\tcheck_for_new_submodule_commits(ref->new_oid.hash);\n \t\tr = s_update_ref(\"fast-forward\", ref, 1);\n-\t\tstrbuf_addf(display, \"%c %-*s %-*s -> %s%s\",\n-\t\t\t    r ? '!' : ' ',\n-\t\t\t    TRANSPORT_SUMMARY_WIDTH, quickref.buf,\n-\t\t\t    REFCOL_WIDTH, remote, pretty_ref,\n-\t\t\t    r ? _(\"  (unable to update local ref)\") : \"\");\n+\t\tformat_display(display, r ? '!' : ' ', quickref.buf,\n+\t\t\t       r ? _(\"unable to update local ref\") : NULL,\n+\t\t\t       remote, pretty_ref);\n \t\tstrbuf_release(&quickref);\n \t\treturn r;\n \t} else if (force || ref->force) {\n@@ -562,18 +564,14 @@ static int update_local_ref(struct ref *ref,\n \t\t    (recurse_submodules != RECURSE_SUBMODULES_ON))\n \t\t\tcheck_for_new_submodule_commits(ref->new_oid.hash);\n \t\tr = s_update_ref(\"forced-update\", ref, 1);\n-\t\tstrbuf_addf(display, \"%c %-*s %-*s -> %s  (%s)\",\n-\t\t\t    r ? '!' : '+',\n-\t\t\t    TRANSPORT_SUMMARY_WIDTH, quickref.buf,\n-\t\t\t    REFCOL_WIDTH, remote, pretty_ref,\n-\t\t\t    r ? _(\"unable to update local ref\") : _(\"forced update\"));\n+\t\tformat_display(display, r ? '!' : '+', quickref.buf,\n+\t\t\t       r ? _(\"unable to update local ref\") : _(\"forced update\"),\n+\t\t\t       remote, pretty_ref);\n \t\tstrbuf_release(&quickref);\n \t\treturn r;\n \t} else {\n-\t\tstrbuf_addf(display, \"! %-*s %-*s -> %s  %s\",\n-\t\t\t    TRANSPORT_SUMMARY(_(\"[rejected]\")),\n-\t\t\t    REFCOL_WIDTH, remote, pretty_ref,\n-\t\t\t    _(\"(non-fast-forward)\"));\n+\t\tformat_display(display, '!', _(\"[rejected]\"), _(\"non-fast-forward\"),\n+\t\t\t       remote, pretty_ref);\n \t\treturn 1;\n \t}\n }\n@@ -714,11 +712,10 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,\n \t\t\t\trc |= update_local_ref(ref, what, rm, &note);\n \t\t\t\tfree(ref);\n \t\t\t} else\n-\t\t\t\tstrbuf_addf(&note, \"* %-*s %-*s -> FETCH_HEAD\",\n-\t\t\t\t\t    TRANSPORT_SUMMARY_WIDTH,\n-\t\t\t\t\t    *kind ? kind : \"branch\",\n-\t\t\t\t\t    REFCOL_WIDTH,\n-\t\t\t\t\t    *what ? what : \"HEAD\");\n+\t\t\t\tformat_display(&note, '*',\n+\t\t\t\t\t       *kind ? kind : \"branch\", NULL,\n+\t\t\t\t\t       *what ? what : \"HEAD\",\n+\t\t\t\t\t       \"FETCH_HEAD\");\n \t\t\tif (note.len) {\n \t\t\t\tif (verbosity >= 0 && !shown_url) {\n \t\t\t\t\tfprintf(stderr, _(\"From %.*s\\n\"),\n@@ -812,13 +809,15 @@ static int prune_refs(struct refspec *refs, int ref_count, struct ref *ref_map,\n \n \tif (verbosity >= 0) {\n \t\tfor (ref = stale_refs; ref; ref = ref->next) {\n+\t\t\tstruct strbuf sb = STRBUF_INIT;\n \t\t\tif (!shown_url) {\n \t\t\t\tfprintf(stderr, _(\"From %.*s\\n\"), url_len, url);\n \t\t\t\tshown_url = 1;\n \t\t\t}\n-\t\t\tfprintf(stderr, \" x %-*s %-*s -> %s\\n\",\n-\t\t\t\tTRANSPORT_SUMMARY(_(\"[deleted]\")),\n-\t\t\t\tREFCOL_WIDTH, _(\"(none)\"), prettify_refname(ref->name));\n+\t\t\tformat_display(&sb, 'x', _(\"[deleted]\"), NULL,\n+\t\t\t\t       _(\"(none)\"), prettify_refname(ref->name));\n+\t\t\tfprintf(stderr, \" %s\\n\",sb.buf);\n+\t\t\tstrbuf_release(&sb);\n \t\t\twarn_dangling_symref(stderr, dangling_msg, ref->name);\n \t\t}\n \t}\n-- \n2.8.2.524.g6ff3d78\n"},{"id":"288220","messageId":"20160603110843.15434-4-pclouds@gmail.com","threadId":"42413","inReplyTo":"20160603110843.15434-1-pclouds@gmail.com","subject":"[PATCH v2 3/3] fetch: reduce duplicate in ref update status lines","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-06-03T11:08:43Z","receivedAt":"2016-06-03T11:08:43Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"When there are lots of ref updates, each has different name length, this\nwill make it easier to look because the variable part is at the end.\n---\n Documentation/git-fetch.txt |  7 +++++++\n builtin/fetch.c             | 37 ++++++++++++++++++++++++++++++++++++-\n t/t5510-fetch.sh            |  4 ++--\n t/t5526-fetch-submodules.sh | 26 +++++++++++++-------------\n 4 files changed, 58 insertions(+), 16 deletions(-)\n\ndiff --git a/Documentation/git-fetch.txt b/Documentation/git-fetch.txt\nindex 18e733c..61c3bd1 100644\n--- a/Documentation/git-fetch.txt\n+++ b/Documentation/git-fetch.txt\n@@ -113,6 +113,13 @@ representing the status of a single ref. Each line is of the form:\n  <flag> <summary> <from> -> <to> (<reason>)\n -------------------------------\n \n+When `from` and `to` share a common suffix, the line could be\n+displayed in the form:\n+\n+-------------------------------\n+ <flag> <summary> {<from> -> <to>}<suffix> (<reason>)\n+-------------------------------\n+\n The status of up-to-date refs is shown only if --verbose option is\n used.\n \ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex a7f152a..15782d6 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -451,12 +451,47 @@ fail:\n \n #define REFCOL_WIDTH  10\n \n+static int common_suffix_length(const char *a, const char *b)\n+{\n+\tconst char *pa = a + strlen(a);\n+\tconst char *pb = b + strlen(b);\n+\tint count = 0;\n+\n+\twhile (pa > a && pb > b && pa[-1] == pb[-1]) {\n+\t\tpa--;\n+\t\tpb--;\n+\t\tcount++;\n+\t}\n+\n+\t/* stick to '/' boundary, do not break in the middle of a word */\n+\twhile (count) {\n+\t\tif (*pa == '/' ||\n+\t\t    (pa == a && pb > b && pb[-1] == '/') ||\n+\t\t    (pb == b && pa > a && pa[-1] == '/'))\n+\t\t\tbreak;\n+\t\tpa++;\n+\t\tpb++;\n+\t\tcount--;\n+\t}\n+\n+\treturn count;\n+}\n+\n static void format_display(struct strbuf *display, char code,\n \t\t\t   const char *summary, const char *error,\n \t\t\t   const char *remote, const char *local)\n {\n+\tint len;\n+\n \tstrbuf_addf(display, \"%c %-*s \", code, TRANSPORT_SUMMARY(summary));\n-\tstrbuf_addf(display, \"%-*s -> %s\", REFCOL_WIDTH, remote, local);\n+\tlen = common_suffix_length(remote, local);\n+\tif (len)\n+\t\tstrbuf_addf(display, \"{%.*s -> %.*s}%s\",\n+\t\t\t    (int)strlen(remote) - len, remote,\n+\t\t\t    (int)strlen(local) - len, local,\n+\t\t\t    remote + strlen(remote) - len);\n+\telse\n+\t\tstrbuf_addf(display, \"%-*s -> %s\", REFCOL_WIDTH, remote, local);\n \tif (error)\n \t\tstrbuf_addf(display, \"  (%s)\", error);\n }\ndiff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\nindex 454d896..9a7649c 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -222,11 +222,11 @@ test_expect_success 'fetch uses remote ref names to describe new refs' '\n \t(\n \t\tcd descriptive &&\n \t\tgit fetch o 2>actual &&\n-\t\tgrep \" -> refs/crazyheads/descriptive-branch$\" actual |\n+\t\tgrep \" -> refs/crazyheads/.descriptive-branch$\" actual |\n \t\ttest_i18ngrep \"new branch\" &&\n \t\tgrep \" -> descriptive-tag$\" actual |\n \t\ttest_i18ngrep \"new tag\" &&\n-\t\tgrep \" -> crazy$\" actual |\n+\t\tgrep \" -> .crazy$\" actual |\n \t\ttest_i18ngrep \"new ref\"\n \t) &&\n \tgit checkout master\ndiff --git a/t/t5526-fetch-submodules.sh b/t/t5526-fetch-submodules.sh\nindex 954d0e4..2285c47 100755\n--- a/t/t5526-fetch-submodules.sh\n+++ b/t/t5526-fetch-submodules.sh\n@@ -18,7 +18,7 @@ add_upstream_commit() {\n \t\thead2=$(git rev-parse --short HEAD) &&\n \t\techo \"Fetching submodule submodule\" > ../expect.err &&\n \t\techo \"From $pwd/submodule\" >> ../expect.err &&\n-\t\techo \"   $head1..$head2  master     -> origin/master\" >> ../expect.err\n+\t\techo \"   $head1..$head2  { -> origin/}master\" >> ../expect.err\n \t) &&\n \t(\n \t\tcd deepsubmodule &&\n@@ -30,7 +30,7 @@ add_upstream_commit() {\n \t\thead2=$(git rev-parse --short HEAD) &&\n \t\techo \"Fetching submodule submodule/subdir/deepsubmodule\" >> ../expect.err\n \t\techo \"From $pwd/deepsubmodule\" >> ../expect.err &&\n-\t\techo \"   $head1..$head2  master     -> origin/master\" >> ../expect.err\n+\t\techo \"   $head1..$head2  { -> origin/}master\" >> ../expect.err\n \t)\n }\n \n@@ -235,7 +235,7 @@ test_expect_success \"Recursion stops when no new submodule commits are fetched\"\n \tgit commit -m \"new submodule\" &&\n \thead2=$(git rev-parse --short HEAD) &&\n \techo \"From $pwd/.\" > expect.err.sub &&\n-\techo \"   $head1..$head2  master     -> origin/master\" >>expect.err.sub &&\n+\techo \"   $head1..$head2  { -> origin/}master\" >>expect.err.sub &&\n \thead -3 expect.err >> expect.err.sub &&\n \t(\n \t\tcd downstream &&\n@@ -253,7 +253,7 @@ test_expect_success \"Recursion doesn't happen when new superproject commits don'\n \tgit commit -m \"new file\" &&\n \thead2=$(git rev-parse --short HEAD) &&\n \techo \"From $pwd/.\" > expect.err.file &&\n-\techo \"   $head1..$head2  master     -> origin/master\" >> expect.err.file &&\n+\techo \"   $head1..$head2  { -> origin/}master\" >> expect.err.file &&\n \t(\n \t\tcd downstream &&\n \t\tgit fetch >../actual.out 2>../actual.err\n@@ -277,7 +277,7 @@ test_expect_success \"Recursion picks up config in submodule\" '\n \tgit commit -m \"new submodule\" &&\n \thead2=$(git rev-parse --short HEAD) &&\n \techo \"From $pwd/.\" > expect.err.sub &&\n-\techo \"   $head1..$head2  master     -> origin/master\" >> expect.err.sub &&\n+\techo \"   $head1..$head2  { -> origin/}master\" >> expect.err.sub &&\n \tcat expect.err >> expect.err.sub &&\n \t(\n \t\tcd downstream &&\n@@ -306,14 +306,14 @@ test_expect_success \"Recursion picks up all submodules when necessary\" '\n \t\thead2=$(git rev-parse --short HEAD) &&\n \t\techo \"Fetching submodule submodule\" > ../expect.err.sub &&\n \t\techo \"From $pwd/submodule\" >> ../expect.err.sub &&\n-\t\techo \"   $head1..$head2  master     -> origin/master\" >> ../expect.err.sub\n+\t\techo \"   $head1..$head2  { -> origin/}master\" >> ../expect.err.sub\n \t) &&\n \thead1=$(git rev-parse --short HEAD) &&\n \tgit add submodule &&\n \tgit commit -m \"new submodule\" &&\n \thead2=$(git rev-parse --short HEAD) &&\n \techo \"From $pwd/.\" > expect.err.2 &&\n-\techo \"   $head1..$head2  master     -> origin/master\" >> expect.err.2 &&\n+\techo \"   $head1..$head2  { -> origin/}master\" >> expect.err.2 &&\n \tcat expect.err.sub >> expect.err.2 &&\n \ttail -3 expect.err >> expect.err.2 &&\n \t(\n@@ -339,7 +339,7 @@ test_expect_success \"'--recurse-submodules=on-demand' doesn't recurse when no ne\n \t\thead2=$(git rev-parse --short HEAD) &&\n \t\techo Fetching submodule submodule > ../expect.err.sub &&\n \t\techo \"From $pwd/submodule\" >> ../expect.err.sub &&\n-\t\techo \"   $head1..$head2  master     -> origin/master\" >> ../expect.err.sub\n+\t\techo \"   $head1..$head2  { -> origin/}master\" >> ../expect.err.sub\n \t) &&\n \t(\n \t\tcd downstream &&\n@@ -358,7 +358,7 @@ test_expect_success \"'--recurse-submodules=on-demand' recurses as deep as necess\n \thead2=$(git rev-parse --short HEAD) &&\n \ttail -3 expect.err > expect.err.deepsub &&\n \techo \"From $pwd/.\" > expect.err &&\n-\techo \"   $head1..$head2  master     -> origin/master\" >>expect.err &&\n+\techo \"   $head1..$head2  { -> origin/}master\" >>expect.err &&\n \tcat expect.err.sub >> expect.err &&\n \tcat expect.err.deepsub >> expect.err &&\n \t(\n@@ -387,7 +387,7 @@ test_expect_success \"'--recurse-submodules=on-demand' stops when no new submodul\n \tgit commit -m \"new file\" &&\n \thead2=$(git rev-parse --short HEAD) &&\n \techo \"From $pwd/.\" > expect.err.file &&\n-\techo \"   $head1..$head2  master     -> origin/master\" >> expect.err.file &&\n+\techo \"   $head1..$head2  { -> origin/}master\" >> expect.err.file &&\n \t(\n \t\tcd downstream &&\n \t\tgit fetch --recurse-submodules=on-demand >../actual.out 2>../actual.err\n@@ -408,7 +408,7 @@ test_expect_success \"'fetch.recurseSubmodules=on-demand' overrides global config\n \tgit commit -m \"new submodule\" &&\n \thead2=$(git rev-parse --short HEAD) &&\n \techo \"From $pwd/.\" > expect.err.2 &&\n-\techo \"   $head1..$head2  master     -> origin/master\" >>expect.err.2 &&\n+\techo \"   $head1..$head2  { -> origin/}master\" >>expect.err.2 &&\n \thead -3 expect.err >> expect.err.2 &&\n \t(\n \t\tcd downstream &&\n@@ -436,7 +436,7 @@ test_expect_success \"'submodule.<sub>.fetchRecurseSubmodules=on-demand' override\n \tgit commit -m \"new submodule\" &&\n \thead2=$(git rev-parse --short HEAD) &&\n \techo \"From $pwd/.\" > expect.err.2 &&\n-\techo \"   $head1..$head2  master     -> origin/master\" >>expect.err.2 &&\n+\techo \"   $head1..$head2  { -> origin/}master\" >>expect.err.2 &&\n \thead -3 expect.err >> expect.err.2 &&\n \t(\n \t\tcd downstream &&\n@@ -462,7 +462,7 @@ test_expect_success \"don't fetch submodule when newly recorded commits are alrea\n \tgit commit -m \"submodule rewound\" &&\n \thead2=$(git rev-parse --short HEAD) &&\n \techo \"From $pwd/.\" > expect.err &&\n-\techo \"   $head1..$head2  master     -> origin/master\" >> expect.err &&\n+\techo \"   $head1..$head2  { -> origin/}master\" >> expect.err &&\n \t(\n \t\tcd downstream &&\n \t\tgit fetch >../actual.out 2>../actual.err\n-- \n2.8.2.524.g6ff3d78\n"},{"id":"288252","messageId":"57519553.10106@xiplink.com","threadId":"42413","inReplyTo":"20160603110843.15434-2-pclouds@gmail.com","subject":"Re: [PATCH v2 1/3] git-fetch.txt: document fetch output","fromName":"Marc Branchaud","fromEmail":"marcnarc@xiplink.com","sentAt":"2016-06-03T14:33:55Z","receivedAt":"2016-06-03T14:33:55Z","isPatch":true,"sender":{"key":"marcnarc@xiplink.com","avatar":"https://avatars.githubusercontent.com/u/14980203?v=4"},"body":"On 2016-06-03 07:08 AM, Nguyễn Thái Ngọc Duy wrote:\n> This documents the ref update status of fetch. The structure of this\n> output is defined in [1]. The ouput content is refined a bit in [2]\n\ns/The ouput/The output/\n\n> [3] [4].\n>\n> This patch is a copy from git-push.txt, modified a bit because the\n> flag '-' means different things in push (delete) and fetch (tag\n> update). We probably should unify the documents at some point in\n> future.\n>\n> PS. For code archaeologists, the discussion mentioned in [1] is\n> probably [5].\n>\n> [1] 165f390 (git-fetch: more terse fetch output - 2007-11-03)\n> [2] 6315472 (fetch: report local storage errors ... - 2008-06-26)\n> [3] f360d84 (builtin-fetch: add --prune option - 2009-11-10)\n> [4] 0997ada (fetch: describe new refs based on where... - 2012-04-16)\n> [5] http://thread.gmane.org/gmane.comp.version-control.git/61657\n> ---\n>   Documentation/git-fetch.txt | 46 +++++++++++++++++++++++++++++++++++++++++++++\n>   1 file changed, 46 insertions(+)\n>\n> diff --git a/Documentation/git-fetch.txt b/Documentation/git-fetch.txt\n> index efe56e0..18e733c 100644\n> --- a/Documentation/git-fetch.txt\n> +++ b/Documentation/git-fetch.txt\n> @@ -99,6 +99,52 @@ The latter use of the `remote.<repository>.fetch` values can be\n>   overridden by giving the `--refmap=<refspec>` parameter(s) on the\n>   command line.\n>\n> +OUTPUT\n> +------\n> +\n> +The output of \"git fetch\" depends on the transport method used; this\n\nWhat a mysterious statement!  Does this tabular format actually change \nwhen fetching over HTTP?  Maybe it's worth documenting the differences?\n\n> +section describes the output when pushing over the Git protocol\n\ns/pushing/fetching/\n\n> +(either locally or via ssh).\n> +\n> +The status of the push is output in tabular form, with each line\n\ns/push/fetch/\n\n> +representing the status of a single ref. Each line is of the form:\n> +\n> +-------------------------------\n> + <flag> <summary> <from> -> <to> (<reason>)\n> +-------------------------------\n> +\n> +The status of up-to-date refs is shown only if --verbose option is\n> +used.\n> +\n> +flag::\n> +\tA single character indicating the status of the ref:\n> +(space);; for a successfully fetched fast-forward;\n> +`+`;; for a successful forced update;\n> +`x`;; for a successfully deleted ref;\n\nI did a double-take here, until I remembered --prune.  Maybe add \"(when \nusing the --prune option)\"?\n\n\t\tM.\n"},{"id":"288253","messageId":"575199E7.7000503@xiplink.com","threadId":"42413","inReplyTo":"20160603110843.15434-4-pclouds@gmail.com","subject":"Re: [PATCH v2 3/3] fetch: reduce duplicate in ref update status lines","fromName":"Marc Branchaud","fromEmail":"marcnarc@xiplink.com","sentAt":"2016-06-03T14:53:27Z","receivedAt":"2016-06-03T14:53:27Z","isPatch":true,"sender":{"key":"marcnarc@xiplink.com","avatar":"https://avatars.githubusercontent.com/u/14980203?v=4"},"body":"On 2016-06-03 07:08 AM, Nguyễn Thái Ngọc Duy wrote:\n> When there are lots of ref updates, each has different name length, this\n> will make it easier to look because the variable part is at the end.\n\ns/look/read/\n\nFor the record, I prefer the earlier stair-step format to this\n\t{xxx -> yyy}/foo\nstuff.\n\nPeff suggested that a two-pass approach might not be too bad, but had \nproblems when he tried it with extra-long refs.  Maybe those problems \ncould be dealt with, and we could get a simple, aligned output?\n\nWhat if we detect when the full line exceeds the terminal width, and \ninsert a newline after the remote ref and indent the ->  to the same \noffset as its surrounding lines, like this:\n\n  * [new branch]      2nd-index -> pclouds/2nd-index\n  * [new branch]      some-kind-of-long-ref-name\n                                -> pclouds/some-kind-of-long-ref-name\n  * [new branch]      3nd-index -> pclouds/3nd-index\n\n> ---\n>   Documentation/git-fetch.txt |  7 +++++++\n>   builtin/fetch.c             | 37 ++++++++++++++++++++++++++++++++++++-\n>   t/t5510-fetch.sh            |  4 ++--\n>   t/t5526-fetch-submodules.sh | 26 +++++++++++++-------------\n>   4 files changed, 58 insertions(+), 16 deletions(-)\n>\n> diff --git a/Documentation/git-fetch.txt b/Documentation/git-fetch.txt\n> index 18e733c..61c3bd1 100644\n> --- a/Documentation/git-fetch.txt\n> +++ b/Documentation/git-fetch.txt\n> @@ -113,6 +113,13 @@ representing the status of a single ref. Each line is of the form:\n>    <flag> <summary> <from> -> <to> (<reason>)\n>   -------------------------------\n>\n> +When `from` and `to` share a common suffix, the line could be\n> +displayed in the form:\n> +\n> +-------------------------------\n> + <flag> <summary> {<from> -> <to>}<suffix> (<reason>)\n\nIf we go with this format, we'll need to document <suffix>.  I'm not \nsure how to describe it succinctly, especially since it's not always \nused.  Really there are two possible output formats:\n\n\t<flag> <summary> {<from> -> <to>}<suffix> (<reason>)\n\t<flag> <summary> <from> -> <to> (<reason>)\n\nThis is starting to feel too complex.\n\n\t\tM.\n"},{"id":"288265","messageId":"xmqqr3cep6at.fsf@gitster.mtv.corp.google.com","threadId":"42413","inReplyTo":"20160603110843.15434-3-pclouds@gmail.com","subject":"Re: [PATCH v2 2/3] fetch: refactor ref update status formatting code","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-06-03T16:48:26Z","receivedAt":"2016-06-03T16:48:26Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> This makes it easier to change the formatting later. And it makes sure\n> translators cannot mess up format specifiers and break Git.\n> ...\n> +static void format_display(struct strbuf *display, char code,\n> +\t\t\t   const char *summary, const char *error,\n> +\t\t\t   const char *remote, const char *local)\n> +{\n> +\tstrbuf_addf(display, \"%c %-*s \", code, TRANSPORT_SUMMARY(summary));\n> +\tstrbuf_addf(display, \"%-*s -> %s\", REFCOL_WIDTH, remote, local);\n> +\tif (error)\n> +\t\tstrbuf_addf(display, \"  (%s)\", error);\n> +}\n> +\n\nNice reduction of the code repetition below.  Looks very sensible.\n"},{"id":"288267","messageId":"20160603165502.GA1733@sigill.intra.peff.net","threadId":"42413","inReplyTo":"20160603110843.15434-2-pclouds@gmail.com","subject":"Re: [PATCH v2 1/3] git-fetch.txt: document fetch output","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2016-06-03T16:55:03Z","receivedAt":"2016-06-03T16:55:03Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Fri, Jun 03, 2016 at 06:08:41PM +0700, Nguyễn Thái Ngọc Duy wrote:\n\n> This patch is a copy from git-push.txt, modified a bit because the\n> flag '-' means different things in push (delete) and fetch (tag\n> update). We probably should unify the documents at some point in\n> future.\n\nThis is stderr output from a porcelain. I don't think we make any\npromises about the format, and since we're changing other bits, perhaps\nnow is a good time to unify the \"-\" handling.\n\n> +The status of the push is output in tabular form, with each line\n> +representing the status of a single ref. Each line is of the form:\n\nLike Marc, I wondered what this means. :) I know that there have been\ndifferent status outputs from transport.c and fetch.c in the past, but I\nthought we had unified a lot of that. If we haven't, it seems like a\ngood time to do so (and I'd prefer fixing to documenting quirks, when\nit's something that isn't plumbing).\n\n-Peff\n"},{"id":"288268","messageId":"xmqqlh2mp5qv.fsf@gitster.mtv.corp.google.com","threadId":"42413","inReplyTo":"20160603110843.15434-4-pclouds@gmail.com","subject":"Re: [PATCH v2 3/3] fetch: reduce duplicate in ref update status lines","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-06-03T17:00:24Z","receivedAt":"2016-06-03T17:00:24Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> +static int common_suffix_length(const char *a, const char *b)\n> +{\n> +\tconst char *pa = a + strlen(a);\n> +\tconst char *pb = b + strlen(b);\n> +\tint count = 0;\n> +\n> +\twhile (pa > a && pb > b && pa[-1] == pb[-1]) {\n> +\t\tpa--;\n> +\t\tpb--;\n> +\t\tcount++;\n> +\t}\n> +\n> +\t/* stick to '/' boundary, do not break in the middle of a word */\n> +\twhile (count) {\n> +\t\tif (*pa == '/' ||\n> +\t\t    (pa == a && pb > b && pb[-1] == '/') ||\n> +\t\t    (pb == b && pa > a && pa[-1] == '/'))\n> +\t\t\tbreak;\n> +\t\tpa++;\n> +\t\tpb++;\n> +\t\tcount--;\n> +\t}\n> +\n> +\treturn count;\n> +}\n> +\n\nWhy do you need two loops, one going backward from the tail and then\ngoing forward toward '/'?  Wouldn't it be sufficient to keep track\nof the last slash you saw in a while scanning backwards?  I.e\nsomething along the lines of:\n\n\ttail_a = a + strlen(a);\n\tfor (pa = tail_a, pb = b + strlen(b), slash_in_a = NULL;\n             a < pa && b < pb && pa[-1] == pb[-1];\n\t     pa--, pb--) {\n\t\tif (*pa == '/')\n\t\t\tslash_in_a = pa;\n\t}\n\tcount = a + strlen(a) - slash_in_a;\n\t\t\nperhaps?\n\n> diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\n> index 454d896..9a7649c 100755\n> --- a/t/t5510-fetch.sh\n> +++ b/t/t5510-fetch.sh\n> @@ -222,11 +222,11 @@ test_expect_success 'fetch uses remote ref names to describe new refs' '\n>  \t(\n>  \t\tcd descriptive &&\n>  \t\tgit fetch o 2>actual &&\n> -\t\tgrep \" -> refs/crazyheads/descriptive-branch$\" actual |\n> +\t\tgrep \" -> refs/crazyheads/.descriptive-branch$\" actual |\n>  \t\ttest_i18ngrep \"new branch\" &&\n>  \t\tgrep \" -> descriptive-tag$\" actual |\n>  \t\ttest_i18ngrep \"new tag\" &&\n> -\t\tgrep \" -> crazy$\" actual |\n> +\t\tgrep \" -> .crazy$\" actual |\n>  \t\ttest_i18ngrep \"new ref\"\n>  \t) &&\n\nThese are somewhat cryptic ;-)\n\nOther than that, the patch looks OK.\n\nThanks.\n"},{"id":"288273","messageId":"20160603170058.GB1733@sigill.intra.peff.net","threadId":"42413","inReplyTo":"20160603110843.15434-1-pclouds@gmail.com","subject":"Re: [PATCH v2 0/3] Better ref summary alignment in \"git fetch\"","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2016-06-03T17:00:58Z","receivedAt":"2016-06-03T17:00:58Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Fri, Jun 03, 2016 at 06:08:40PM +0700, Nguyễn Thái Ngọc Duy wrote:\n\n> v2 reformats \"abc/common -> def/common\" to \"{abc -> def}/common\"\n> instead and fall back to \"a -> b\" when they have nothing in commmon\n> (e.g. \"HEAD -> FETCH_HEAD\"). We could add an option if a user wants to\n> stick with \"a -> b\" (and we could do some alignment there as well) but\n> it's not part of this series.\n\nI tried this on one of my nastiest long-branch-heavy repos. It really\ndoes look better than the status quo. I think it's nicer than the\nstair-stepping proposal in that it does get everything aligned\nperfectly, and reduces wasted data that spills off the side of my\nterminal.\n\nI do somehow find the:\n\n  { -> origin}/whatever\n\na little off-putting, I think because the \"from\" side is empty, and\ntherefore you get this weird mix of punctuation: \"{ ->\". I wonder if\nthere's another syntactic shorthand we could apply in that case\n(especially since it will easily be the most common), but I couldn't\nthink of one.\n\n> It's a shame that the flag '-' in these ref update lines is not the\n> same in fetch and push (see 1/3). Because git-fetch does not support\n> --porcelain option, maybe it's not too late to change its meaning...  \n\nI'd agree with that final \"maybe\". :)\n\n-Peff\n"},{"id":"288269","messageId":"xmqqh9dap5jk.fsf@gitster.mtv.corp.google.com","threadId":"42413","inReplyTo":"575199E7.7000503@xiplink.com","subject":"Re: [PATCH v2 3/3] fetch: reduce duplicate in ref update status lines","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-06-03T17:04:47Z","receivedAt":"2016-06-03T17:04:47Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Marc Branchaud <marcnarc@xiplink.com> writes:\n\n> What if we detect when the full line exceeds the terminal width, and\n> insert a newline after the remote ref and indent the ->  to the same\n> offset as its surrounding lines, like this:\n>\n>  * [new branch]      2nd-index -> pclouds/2nd-index\n>  * [new branch]      some-kind-of-long-ref-name\n>                                -> pclouds/some-kind-of-long-ref-name\n>  * [new branch]      3nd-index -> pclouds/3nd-index\n\nI am OK with this format (not in the sense that I like it better\nthan what the patch produces, but in the sense that I do not have\nstrong preference either way).  It may be hard to come up with a\ngood heuristics to decide where on the display width \"->\" should\ncome, though.\n\n>> +When `from` and `to` share a common suffix, the line could be\n>> +displayed in the form:\n>> +\n>> +-------------------------------\n>> + <flag> <summary> {<from> -> <to>}<suffix> (<reason>)\n>\n> If we go with this format, we'll need to document <suffix>.\n\nThe sentence above calls it \"a common suffix\", so instead of saying\n<suffix> we can say <common-suffix> perhaps?  Or did you mean\nsomething more than that?\n"},{"id":"288270","messageId":"20160603170621.GA3858@sigill.intra.peff.net","threadId":"42413","inReplyTo":"20160603110843.15434-4-pclouds@gmail.com","subject":"Re: [PATCH v2 3/3] fetch: reduce duplicate in ref update status lines","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2016-06-03T17:06:21Z","receivedAt":"2016-06-03T17:06:21Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Fri, Jun 03, 2016 at 06:08:43PM +0700, Nguyễn Thái Ngọc Duy wrote:\n\n> When there are lots of ref updates, each has different name length, this\n> will make it easier to look because the variable part is at the end.\n\nIs it worth handling more complicated cases, where there is a similar\n\"middle\", but different beginning?\n\nOne of my common refspecs is:\n\n  +refs/pull/*/head:refs/remotes/pull/*\n\nThat still shows as:\n\n  refs/pull/123/head -> pull/123\n\nbut could be:\n\n  {refs -> }/pull/123{head -> }\n\nI actually think that _isn't_ an improvement, but I wonder if there is a\nformat that would be.\n\n-Peff\n"},{"id":"288278","messageId":"xmqqd1nyp415.fsf@gitster.mtv.corp.google.com","threadId":"42413","inReplyTo":"20160603170058.GB1733@sigill.intra.peff.net","subject":"Re: [PATCH v2 0/3] Better ref summary alignment in \"git fetch\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-06-03T17:37:26Z","receivedAt":"2016-06-03T17:37:26Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n>> It's a shame that the flag '-' in these ref update lines is not the\n>> same in fetch and push (see 1/3). Because git-fetch does not support\n>> --porcelain option, maybe it's not too late to change its meaning...  \n>\n> I'd agree with that final \"maybe\". :)\n\nYeah, this is purely for human consumption (which is why we see\nother changes in this series in the first place), no?  So I'd agree\nwith that final \"maybe\" too ;-).\n"},{"id":"288289","messageId":"5751E1C1.8080507@xiplink.com","threadId":"42413","inReplyTo":"xmqqh9dap5jk.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v2 3/3] fetch: reduce duplicate in ref update status lines","fromName":"Marc Branchaud","fromEmail":"marcnarc@xiplink.com","sentAt":"2016-06-03T20:00:01Z","receivedAt":"2016-06-03T20:00:01Z","isPatch":true,"sender":{"key":"marcnarc@xiplink.com","avatar":"https://avatars.githubusercontent.com/u/14980203?v=4"},"body":"On 2016-06-03 01:04 PM, Junio C Hamano wrote:\n> Marc Branchaud <marcnarc@xiplink.com> writes:\n>\n>> What if we detect when the full line exceeds the terminal width, and\n>> insert a newline after the remote ref and indent the ->  to the same\n>> offset as its surrounding lines, like this:\n>>\n>>   * [new branch]      2nd-index -> pclouds/2nd-index\n>>   * [new branch]      some-kind-of-long-ref-name\n>>                                 -> pclouds/some-kind-of-long-ref-name\n>>   * [new branch]      3nd-index -> pclouds/3nd-index\n>\n> I am OK with this format (not in the sense that I like it better\n> than what the patch produces, but in the sense that I do not have\n> strong preference either way).  It may be hard to come up with a\n> good heuristics to decide where on the display width \"->\" should\n> come, though.\n\nI think aligning it with the -> on the other lines makes the most \naesthetic sense.\n\nAre you worried that the right-hand-side ref might still wrap?  I'm not \ntoo concerned about that -- there'll always be the possibility of a ref \nname that's longer than the terminal.\n\n>>> +When `from` and `to` share a common suffix, the line could be\n>>> +displayed in the form:\n>>> +\n>>> +-------------------------------\n>>> + <flag> <summary> {<from> -> <to>}<suffix> (<reason>)\n>>\n>> If we go with this format, we'll need to document <suffix>.\n>\n> The sentence above calls it \"a common suffix\", so instead of saying\n> <suffix> we can say <common-suffix> perhaps?  Or did you mean\n> something more than that?\n\nI missed that, and although I think it's an adequate description I think \nmost readers will miss it too.  They eye tends to notice the \nsyntax-description bits then skip down to the list of element \ndescriptions to understand which bits mean what.  My brain wants to find \n\"suffix\" in that list.\n\nAnyway, not a major issue, really.\n\n\t\tM.\n"},{"id":"288293","messageId":"xmqqtwhangdn.fsf@gitster.mtv.corp.google.com","threadId":"42413","inReplyTo":"5751E1C1.8080507@xiplink.com","subject":"Re: [PATCH v2 3/3] fetch: reduce duplicate in ref update status lines","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-06-03T20:53:40Z","receivedAt":"2016-06-03T20:53:40Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Marc Branchaud <marcnarc@xiplink.com> writes:\n\n>>>   * [new branch]      2nd-index -> pclouds/2nd-index\n>>>   * [new branch]      some-kind-of-long-ref-name\n>>>                                 -> pclouds/some-kind-of-long-ref-name\n>>>   * [new branch]      3nd-index -> pclouds/3nd-index\n> ...\n> I think aligning it with the -> on the other lines makes the most\n> aesthetic sense.\n>\n> Are you worried that the right-hand-side ref might still wrap?\n\nNot really.\n\nGiven that we reserve some fixed screen real estate on the left hand\nside for the sign and explanation (like [new branch]) and 4 columns\nin the middle for \" -> \", we have around 80-25=55 columns to work\nwith to fit \"2nd-index\" and such twice plus \"pclouds/\" once.\nAssuming that remote names are not overly long (say, around 10\ncolumns), that leaves about 20-25 columns for the branch name\nproper.\n\nBy punting on the effort to find a readable format that does not\nrepeat the same info twice, we are sending a signal to the users\nthat they cannot use a meaningful sentence as the name of a branch\nname; they need to stay within a relatively short (i.e. 1/4 of a\nline width) branch name, to avoid triggering this multi-line\nbehaviour.\n\nThe same 55 columns minus remote name, e.g. around 40 columns, could\nhave been used to express the branch name once, if we managed to\nfind a non-redundant format.\n\nThat is what bothers me somewhat.\n"},{"id":"288353","messageId":"CACsJy8BiriuKyrP=0RcbFcSC9WSfcfDeKa-GsKosWxOQdpUZ4g@mail.gmail.com","threadId":"42413","inReplyTo":"xmqqlh2mp5qv.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v2 3/3] fetch: reduce duplicate in ref update status lines","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2016-06-03T23:49:15Z","receivedAt":"2016-06-03T23:49:15Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Sat, Jun 4, 2016 at 12:00 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n>\n> > +static int common_suffix_length(const char *a, const char *b)\n> > +{\n> > +     const char *pa = a + strlen(a);\n> > +     const char *pb = b + strlen(b);\n> > +     int count = 0;\n> > +\n> > +     while (pa > a && pb > b && pa[-1] == pb[-1]) {\n> > +             pa--;\n> > +             pb--;\n> > +             count++;\n> > +     }\n> > +\n> > +     /* stick to '/' boundary, do not break in the middle of a word */\n> > +     while (count) {\n> > +             if (*pa == '/' ||\n> > +                 (pa == a && pb > b && pb[-1] == '/') ||\n> > +                 (pb == b && pa > a && pa[-1] == '/'))\n> > +                     break;\n> > +             pa++;\n> > +             pb++;\n> > +             count--;\n> > +     }\n> > +\n> > +     return count;\n> > +}\n> > +\n>\n> Why do you need two loops, one going backward from the tail and then\n> going forward toward '/'?\n\nI wanted to check something else, then settled for slashes, but the\ntwo loops remained.\n\n> Wouldn't it be sufficient to keep track\n> of the last slash you saw in a while scanning backwards?  I.e\n> something along the lines of:\n>\n>         tail_a = a + strlen(a);\n>         for (pa = tail_a, pb = b + strlen(b), slash_in_a = NULL;\n>              a < pa && b < pb && pa[-1] == pb[-1];\n>              pa--, pb--) {\n>                 if (*pa == '/')\n>                         slash_in_a = pa;\n>         }\n>         count = a + strlen(a) - slash_in_a;\n>\n> perhaps?\n\nActually, Jeff's suggestion about common prefix makes me realize, why\nnot reuse diff.c:pprint_rename()? Your {a -> b} idea came from that.\nWe may need some tweaking there because right now it will not show {\n-> origin/}master.\n\n> > diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\n> > index 454d896..9a7649c 100755\n> > --- a/t/t5510-fetch.sh\n> > +++ b/t/t5510-fetch.sh\n> > @@ -222,11 +222,11 @@ test_expect_success 'fetch uses remote ref names to describe new refs' '\n> >       (\n> >               cd descriptive &&\n> >               git fetch o 2>actual &&\n> > -             grep \" -> refs/crazyheads/descriptive-branch$\" actual |\n> > +             grep \" -> refs/crazyheads/.descriptive-branch$\" actual |\n> >               test_i18ngrep \"new branch\" &&\n> >               grep \" -> descriptive-tag$\" actual |\n> >               test_i18ngrep \"new tag\" &&\n> > -             grep \" -> crazy$\" actual |\n> > +             grep \" -> .crazy$\" actual |\n> >               test_i18ngrep \"new ref\"\n> >       ) &&\n>\n> These are somewhat cryptic ;-)\n\nYeah... too lazy to check if } needs to be quoted or not :D\n-- \nDuy\n"},{"id":"288354","messageId":"CACsJy8DV5aqsxM9+QZ+5d2fMLYF4foBnAxmyiYHOLtqzvS3QGQ@mail.gmail.com","threadId":"42413","inReplyTo":"20160603170621.GA3858@sigill.intra.peff.net","subject":"Re: [PATCH v2 3/3] fetch: reduce duplicate in ref update status lines","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2016-06-03T23:52:23Z","receivedAt":"2016-06-03T23:52:23Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Sat, Jun 4, 2016 at 12:06 AM, Jeff King <peff@peff.net> wrote:\n> On Fri, Jun 03, 2016 at 06:08:43PM +0700, Nguyễn Thái Ngọc Duy wrote:\n>\n>> When there are lots of ref updates, each has different name length, this\n>> will make it easier to look because the variable part is at the end.\n>\n> Is it worth handling more complicated cases, where there is a similar\n> \"middle\", but different beginning?\n>\n> One of my common refspecs is:\n>\n>   +refs/pull/*/head:refs/remotes/pull/*\n>\n> That still shows as:\n>\n>   refs/pull/123/head -> pull/123\n>\n> but could be:\n>\n>   {refs -> }/pull/123{head -> }\n>\n> I actually think that _isn't_ an improvement, but I wonder if there is a\n> format that would be.\n\nA placeholder can still keep the variable part at the end, e.g.\n\"refs/$/head -> pull/123\"\n-- \nDuy\n"},{"id":"288357","messageId":"20160604003126.GA10430@ash","threadId":"42413","inReplyTo":"575199E7.7000503@xiplink.com","subject":"Re: [PATCH v2 3/3] fetch: reduce duplicate in ref update status lines","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2016-06-04T00:31:26Z","receivedAt":"2016-06-04T00:31:26Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Fri, Jun 03, 2016 at 10:53:27AM -0400, Marc Branchaud wrote:\n> On 2016-06-03 07:08 AM, Nguyễn Thái Ngọc Duy wrote:\n> > When there are lots of ref updates, each has different name length, this\n> > will make it easier to look because the variable part is at the end.\n> \n> s/look/read/\n> \n> For the record, I prefer the earlier stair-step format to this\n> \t{xxx -> yyy}/foo\n> stuff.\n> \n> Peff suggested that a two-pass approach might not be too bad, but had \n> problems when he tried it with extra-long refs.  Maybe those problems \n> could be dealt with, and we could get a simple, aligned output?\n\nThe good thing about 2/3 is we can customize the output easily and\neven switch format with config variables. But let's play without\nconfig vars for now. A 3/3 replacement that adds another pass to\ncalculate column width is at the end.\n\n> What if we detect when the full line exceeds the terminal width, and \n> insert a newline after the remote ref and indent the ->  to the same \n> offset as its surrounding lines, like this:\n> \n>   * [new branch]      2nd-index -> pclouds/2nd-index\n>   * [new branch]      some-kind-of-long-ref-name\n>                                 -> pclouds/some-kind-of-long-ref-name\n>   * [new branch]      3nd-index -> pclouds/3nd-index\n\nThe patch does not do fancy stuff like this yet, but it can because\nlines exceeding terminal width is already excluded from column width\ncalculation. So far the output looks good on my terminal (192 chars,\ncan't overflow or refnames are insanely long)\n\n-- 8< --\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex a7f152a..5e1e5c9 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -15,6 +15,7 @@\n #include \"submodule.h\"\n #include \"connected.h\"\n #include \"argv-array.h\"\n+#include \"utf8.h\"\n \n static const char * const builtin_fetch_usage[] = {\n \tN_(\"git fetch [<options>] [<repository> [<refspec>...]]\"),\n@@ -449,14 +450,26 @@ fail:\n \t\t\t   : STORE_REF_ERROR_OTHER;\n }\n \n-#define REFCOL_WIDTH  10\n+static int refcol_width = 10;\n+\n+static void adjust_refcol_width(const char *remote, const char *local)\n+{\n+\tint max = term_columns();\n+\tint rlen = utf8_strwidth(remote);\n+\tint llen = utf8_strwidth(local);\n+\n+\tif (21 /* flag summary */ + rlen + 4 /* => */ + llen >= max)\n+\t\treturn;\n+\tif (refcol_width < rlen)\n+\t\trefcol_width = rlen;\n+}\n \n static void format_display(struct strbuf *display, char code,\n \t\t\t   const char *summary, const char *error,\n \t\t\t   const char *remote, const char *local)\n {\n \tstrbuf_addf(display, \"%c %-*s \", code, TRANSPORT_SUMMARY(summary));\n-\tstrbuf_addf(display, \"%-*s -> %s\", REFCOL_WIDTH, remote, local);\n+\tstrbuf_addf(display, \"%-*s -> %s\", refcol_width, remote, local);\n \tif (error)\n \t\tstrbuf_addf(display, \"  (%s)\", error);\n }\n@@ -618,6 +631,20 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,\n \t\tgoto abort;\n \t}\n \n+\t/*\n+\t * go through all refs to determine column size for\n+\t * \"remote -> local\" output\n+\t */\n+\tfor (rm = ref_map; rm; rm = rm->next) {\n+\t\tif (rm->status == REF_STATUS_REJECT_SHALLOW ||\n+\t\t    !rm->peer_ref ||\n+\t\t    !strcmp(rm->name, \"HEAD\"))\n+\t\t\tcontinue;\n+\n+\t\tadjust_refcol_width(prettify_refname(rm->name),\n+\t\t\t\t    prettify_refname(rm->peer_ref->name));\n+\t}\n+\n \t/*\n \t * We do a pass for each fetch_head_status type in their enum order, so\n \t * merged entries are written before not-for-merge. That lets readers\n-- 8< --\n"},{"id":"288360","messageId":"CACsJy8DFE=NPB8+VfLFf_v719Rz8+at_daWyQ3jQUHYrB6TWZw@mail.gmail.com","threadId":"42413","inReplyTo":"xmqqtwhangdn.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v2 3/3] fetch: reduce duplicate in ref update status lines","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2016-06-04T03:11:58Z","receivedAt":"2016-06-04T03:11:58Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Sat, Jun 4, 2016 at 3:53 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> By punting on the effort to find a readable format that does not\n> repeat the same info twice, we are sending a signal to the users\n> that they cannot use a meaningful sentence as the name of a branch\n> name; they need to stay within a relatively short (i.e. 1/4 of a\n> line width) branch name, to avoid triggering this multi-line\n> behaviour.\n\nThis is subjective because line length (or terminal width) varies. And\nin the time where GUI and web interfaces are also used and preferred\nby some, CLI users may not be able to do much with such a signal\n(can't force everybody to suit you).\n-- \nDuy\n"},{"id":"288361","messageId":"CAPc5daXQsWWuaune6w6_9vd_iywwjpXEUoJ3MoKntjenwCAH2A@mail.gmail.com","threadId":"42413","inReplyTo":"CACsJy8DV5aqsxM9+QZ+5d2fMLYF4foBnAxmyiYHOLtqzvS3QGQ@mail.gmail.com","subject":"Re: [PATCH v2 3/3] fetch: reduce duplicate in ref update status lines","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-06-04T04:53:02Z","receivedAt":"2016-06-04T04:53:02Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"On Fri, Jun 3, 2016 at 4:52 PM, Duy Nguyen <pclouds@gmail.com> wrote:\n> A placeholder can still keep the variable part at the end, e.g.\n> \"refs/$/head -> pull/123\"\n\nI somehow like this very much.\n\nA more typical \"their topic went to remote-tracking namespace under 'origin'\",\naka\n     topic -> origin/topic\naka\n     { -> origin/}topic\nwould look like a more concise\n     topic -> origin/$\nwithout the funny {} notation.\n"},{"id":"288368","messageId":"xmqqeg8czzl4.fsf@gitster.mtv.corp.google.com","threadId":"42413","inReplyTo":"20160604003126.GA10430@ash","subject":"Re: [PATCH v2 3/3] fetch: reduce duplicate in ref update status lines","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-06-04T16:30:15Z","receivedAt":"2016-06-04T16:30:15Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Duy Nguyen <pclouds@gmail.com> writes:\n\n>> Peff suggested that a two-pass approach might not be too bad, but had \n>> problems when he tried it with extra-long refs.  Maybe those problems \n>> could be dealt with, and we could get a simple, aligned output?\n>\n> The good thing about 2/3 is we can customize the output easily and\n> even switch format with config variables. But let's play without\n> config vars for now. A 3/3 replacement that adds another pass to\n> calculate column width is at the end.\n\nYes, I agree with you that 2/3 lays a very good groundwork,\nregardless of the final output format.\n\n> The patch does not do fancy stuff like this yet, but it can because\n> lines exceeding terminal width is already excluded from column width\n> calculation. So far the output looks good on my terminal (192 chars,\n> can't overflow or refnames are insanely long)\n\nSounds like a sensible approach....\n\n> -- 8< --\n> diff --git a/builtin/fetch.c b/builtin/fetch.c\n> index a7f152a..5e1e5c9 100644\n> --- a/builtin/fetch.c\n> +++ b/builtin/fetch.c\n> @@ -15,6 +15,7 @@\n>  #include \"submodule.h\"\n>  #include \"connected.h\"\n>  #include \"argv-array.h\"\n> +#include \"utf8.h\"\n>  \n>  static const char * const builtin_fetch_usage[] = {\n>  \tN_(\"git fetch [<options>] [<repository> [<refspec>...]]\"),\n> @@ -449,14 +450,26 @@ fail:\n>  \t\t\t   : STORE_REF_ERROR_OTHER;\n>  }\n>  \n> -#define REFCOL_WIDTH  10\n> +static int refcol_width = 10;\n> +\n> +static void adjust_refcol_width(const char *remote, const char *local)\n> +{\n> +\tint max = term_columns();\n> +\tint rlen = utf8_strwidth(remote);\n> +\tint llen = utf8_strwidth(local);\n> +\n> +\tif (21 /* flag summary */ + rlen + 4 /* => */ + llen >= max)\n> +\t\treturn;\n> +\tif (refcol_width < rlen)\n> +\t\trefcol_width = rlen;\n> +}\n>  \n>  static void format_display(struct strbuf *display, char code,\n>  \t\t\t   const char *summary, const char *error,\n>  \t\t\t   const char *remote, const char *local)\n>  {\n>  \tstrbuf_addf(display, \"%c %-*s \", code, TRANSPORT_SUMMARY(summary));\n> -\tstrbuf_addf(display, \"%-*s -> %s\", REFCOL_WIDTH, remote, local);\n> +\tstrbuf_addf(display, \"%-*s -> %s\", refcol_width, remote, local);\n\n... and if I understand correctly, this is the only place where you\nneed to decide if you need to switch to two lines, right?  You would\nmeasure width of the remote and compare it with refcol_width or\nsomething like that.\n\n>  \tif (error)\n>  \t\tstrbuf_addf(display, \"  (%s)\", error);\n>  }\n> @@ -618,6 +631,20 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,\n>  \t\tgoto abort;\n>  \t}\n>  \n> +\t/*\n> +\t * go through all refs to determine column size for\n> +\t * \"remote -> local\" output\n> +\t */\n> +\tfor (rm = ref_map; rm; rm = rm->next) {\n> +\t\tif (rm->status == REF_STATUS_REJECT_SHALLOW ||\n> +\t\t    !rm->peer_ref ||\n> +\t\t    !strcmp(rm->name, \"HEAD\"))\n> +\t\t\tcontinue;\n> +\n> +\t\tadjust_refcol_width(prettify_refname(rm->name),\n> +\t\t\t\t    prettify_refname(rm->peer_ref->name));\n> +\t}\n> +\n>  \t/*\n>  \t * We do a pass for each fetch_head_status type in their enum order, so\n>  \t * merged entries are written before not-for-merge. That lets readers\n> -- 8< --\n"},{"id":"288372","messageId":"20160605031141.23513-1-pclouds@gmail.com","threadId":"42413","inReplyTo":"20160603110843.15434-1-pclouds@gmail.com","subject":"[PATCH v3 0/6] Better ref summary alignment in \"git fetch\"","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-06-05T03:11:35Z","receivedAt":"2016-06-05T03:11:35Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This is so people can play with new output format... v3 adds one extra\npass to calculate width (4/6) and swap flag code 'x' and '-' with '-'\nand 't' to align with push flag code (3/6). These are definitely good\nimprovements.\n\n5/6 and 6/6 add two new formats \"{ -> origin}/abc\" and \"abc -> origin/$\".\nFormat output is selected by config key fetch.output. I'm tempted to\nreplace '$' with '&' as it usually means \"what was matched\" in s///\n\nI don't think we'll want both new formats in the end. One of them (or\na new format) will win as the optional format. The aligned \"remote ->\nlocal\" should stay default format. And later on we could break lines\nat \"->\" for very long lines as Marc suggested.\n\nNguyễn Thái Ngọc Duy (6):\n  git-fetch.txt: document fetch output\n  fetch: refactor ref update status formatting code\n  fetch: change flag code for displaying tag update and deleted ref\n  fetch: align all \"remote -> local\" output\n  fetch: reduce duplicate in ref update status lines with { -> }\n  fetch: reduce duplicate in ref update status lines with placeholder\n\n Documentation/config.txt    |   4 +\n Documentation/git-fetch.txt |  54 ++++++++++\n builtin/fetch.c             | 233 ++++++++++++++++++++++++++++++++++++--------\n 3 files changed, 251 insertions(+), 40 deletions(-)\n\n-- \n2.8.2.524.g6ff3d78\n"},{"id":"288373","messageId":"20160605031141.23513-2-pclouds@gmail.com","threadId":"42413","inReplyTo":"20160605031141.23513-1-pclouds@gmail.com","subject":"[PATCH v3 1/6] git-fetch.txt: document fetch output","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-06-05T03:11:36Z","receivedAt":"2016-06-05T03:11:36Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This documents the ref update status of fetch. The structure of this\noutput is defined in [1]. The ouput content is refined a bit in [2]\n[3] [4].\n\nThis patch is a copy from git-push.txt, modified a bit because the\nflag '-' means different things in push (delete) and fetch (tag\nupdate).\n\nPS. For code archaeologists, the discussion mentioned in [1] is\nprobably [5].\n\n[1] 165f390 (git-fetch: more terse fetch output - 2007-11-03)\n[2] 6315472 (fetch: report local storage errors ... - 2008-06-26)\n[3] f360d84 (builtin-fetch: add --prune option - 2009-11-10)\n[4] 0997ada (fetch: describe new refs based on where... - 2012-04-16)\n[5] http://thread.gmane.org/gmane.comp.version-control.git/61657\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-fetch.txt | 46 +++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 46 insertions(+)\n\ndiff --git a/Documentation/git-fetch.txt b/Documentation/git-fetch.txt\nindex efe56e0..a0d0539 100644\n--- a/Documentation/git-fetch.txt\n+++ b/Documentation/git-fetch.txt\n@@ -99,6 +99,52 @@ The latter use of the `remote.<repository>.fetch` values can be\n overridden by giving the `--refmap=<refspec>` parameter(s) on the\n command line.\n \n+OUTPUT\n+------\n+\n+The output of \"git fetch\" depends on the transport method used; this\n+section describes the output when fetch over the Git protocol (either\n+locally or via ssh).\n+\n+The status of the fetch is output in tabular form, with each line\n+representing the status of a single ref. Each line is of the form:\n+\n+-------------------------------\n+ <flag> <summary> <from> -> <to> (<reason>)\n+-------------------------------\n+\n+The status of up-to-date refs is shown only if --verbose option is\n+used.\n+\n+flag::\n+\tA single character indicating the status of the ref:\n+(space);; for a successfully fetched fast-forward;\n+`+`;; for a successful forced update;\n+`x`;; for a successfully deleted ref;\n+`-`;; for a successful tag update;\n+`*`;; for a successfully fetched new ref;\n+`!`;; for a ref that was rejected or failed to update; and\n+`=`;; for a ref that was up to date and did not need fetching.\n+\n+summary::\n+\tFor a successfully fetched ref, the summary shows the old and new\n+\tvalues of the ref in a form suitable for using as an argument to\n+\t`git log` (this is `<old>..<new>` in most cases, and\n+\t`<old>...<new>` for forced non-fast-forward updates).\n+\n+from::\n+\tThe name of the remote ref being fetched from, minus its\n+\t`refs/<type>/` prefix. In the case of deletion, the name of\n+\tthe remote ref is \"(none)\".\n+\n+to::\n+\tThe name of the local ref being updated, minus its\n+\t`refs/<type>/` prefix.\n+\n+reason::\n+\tA human-readable explanation. In the case of successfully fetched\n+\trefs, no explanation is needed. For a failed ref, the reason for\n+\tfailure is described.\n \n EXAMPLES\n --------\n-- \n2.8.2.524.g6ff3d78\n"},{"id":"288374","messageId":"20160605031141.23513-3-pclouds@gmail.com","threadId":"42413","inReplyTo":"20160605031141.23513-1-pclouds@gmail.com","subject":"[PATCH v3 2/6] fetch: refactor ref update status formatting code","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-06-05T03:11:37Z","receivedAt":"2016-06-05T03:11:37Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This makes it easier to change the formatting later. And it makes sure\ntranslators cannot mess up format specifiers and break Git.\n\nThere are a couple call sites where the length of the second column is\nTRANSPORT_SUMMARY_WIDTH instead of calculated by TRANSPORT_SUMMARY(),\nwhich is enforced now. The result should be the same because these call\nsites do not contain characters outside ASCII range.\n\nThe two strbuf_addf() calls instead of one is mostly to reduce\ndiff-noise in a future patch where \"ref -> ref\" is reformatted\ndifferently.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/fetch.c | 77 ++++++++++++++++++++++++++++-----------------------------\n 1 file changed, 38 insertions(+), 39 deletions(-)\n\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 1582ca7..a7f152a 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -451,6 +451,16 @@ fail:\n \n #define REFCOL_WIDTH  10\n \n+static void format_display(struct strbuf *display, char code,\n+\t\t\t   const char *summary, const char *error,\n+\t\t\t   const char *remote, const char *local)\n+{\n+\tstrbuf_addf(display, \"%c %-*s \", code, TRANSPORT_SUMMARY(summary));\n+\tstrbuf_addf(display, \"%-*s -> %s\", REFCOL_WIDTH, remote, local);\n+\tif (error)\n+\t\tstrbuf_addf(display, \"  (%s)\", error);\n+}\n+\n static int update_local_ref(struct ref *ref,\n \t\t\t    const char *remote,\n \t\t\t    const struct ref *remote_ref,\n@@ -467,9 +477,8 @@ static int update_local_ref(struct ref *ref,\n \n \tif (!oidcmp(&ref->old_oid, &ref->new_oid)) {\n \t\tif (verbosity > 0)\n-\t\t\tstrbuf_addf(display, \"= %-*s %-*s -> %s\",\n-\t\t\t\t    TRANSPORT_SUMMARY(_(\"[up to date]\")),\n-\t\t\t\t    REFCOL_WIDTH, remote, pretty_ref);\n+\t\t\tformat_display(display, '=', _(\"[up to date]\"), NULL,\n+\t\t\t\t       remote, pretty_ref);\n \t\treturn 0;\n \t}\n \n@@ -481,10 +490,9 @@ static int update_local_ref(struct ref *ref,\n \t\t * If this is the head, and it's not okay to update\n \t\t * the head, and the old value of the head isn't empty...\n \t\t */\n-\t\tstrbuf_addf(display,\n-\t\t\t    _(\"! %-*s %-*s -> %s  (can't fetch in current branch)\"),\n-\t\t\t    TRANSPORT_SUMMARY(_(\"[rejected]\")),\n-\t\t\t    REFCOL_WIDTH, remote, pretty_ref);\n+\t\tformat_display(display, '!', _(\"[rejected]\"),\n+\t\t\t       _(\"can't fetch in current branch\"),\n+\t\t\t       remote, pretty_ref);\n \t\treturn 1;\n \t}\n \n@@ -492,11 +500,9 @@ static int update_local_ref(struct ref *ref,\n \t    starts_with(ref->name, \"refs/tags/\")) {\n \t\tint r;\n \t\tr = s_update_ref(\"updating tag\", ref, 0);\n-\t\tstrbuf_addf(display, \"%c %-*s %-*s -> %s%s\",\n-\t\t\t    r ? '!' : '-',\n-\t\t\t    TRANSPORT_SUMMARY(_(\"[tag update]\")),\n-\t\t\t    REFCOL_WIDTH, remote, pretty_ref,\n-\t\t\t    r ? _(\"  (unable to update local ref)\") : \"\");\n+\t\tformat_display(display, r ? '!' : '-', _(\"[tag update]\"),\n+\t\t\t       r ? _(\"unable to update local ref\") : NULL,\n+\t\t\t       remote, pretty_ref);\n \t\treturn r;\n \t}\n \n@@ -527,11 +533,9 @@ static int update_local_ref(struct ref *ref,\n \t\t    (recurse_submodules != RECURSE_SUBMODULES_ON))\n \t\t\tcheck_for_new_submodule_commits(ref->new_oid.hash);\n \t\tr = s_update_ref(msg, ref, 0);\n-\t\tstrbuf_addf(display, \"%c %-*s %-*s -> %s%s\",\n-\t\t\t    r ? '!' : '*',\n-\t\t\t    TRANSPORT_SUMMARY(what),\n-\t\t\t    REFCOL_WIDTH, remote, pretty_ref,\n-\t\t\t    r ? _(\"  (unable to update local ref)\") : \"\");\n+\t\tformat_display(display, r ? '!' : '*', what,\n+\t\t\t       r ? _(\"unable to update local ref\") : NULL,\n+\t\t\t       remote, pretty_ref);\n \t\treturn r;\n \t}\n \n@@ -545,11 +549,9 @@ static int update_local_ref(struct ref *ref,\n \t\t    (recurse_submodules != RECURSE_SUBMODULES_ON))\n \t\t\tcheck_for_new_submodule_commits(ref->new_oid.hash);\n \t\tr = s_update_ref(\"fast-forward\", ref, 1);\n-\t\tstrbuf_addf(display, \"%c %-*s %-*s -> %s%s\",\n-\t\t\t    r ? '!' : ' ',\n-\t\t\t    TRANSPORT_SUMMARY_WIDTH, quickref.buf,\n-\t\t\t    REFCOL_WIDTH, remote, pretty_ref,\n-\t\t\t    r ? _(\"  (unable to update local ref)\") : \"\");\n+\t\tformat_display(display, r ? '!' : ' ', quickref.buf,\n+\t\t\t       r ? _(\"unable to update local ref\") : NULL,\n+\t\t\t       remote, pretty_ref);\n \t\tstrbuf_release(&quickref);\n \t\treturn r;\n \t} else if (force || ref->force) {\n@@ -562,18 +564,14 @@ static int update_local_ref(struct ref *ref,\n \t\t    (recurse_submodules != RECURSE_SUBMODULES_ON))\n \t\t\tcheck_for_new_submodule_commits(ref->new_oid.hash);\n \t\tr = s_update_ref(\"forced-update\", ref, 1);\n-\t\tstrbuf_addf(display, \"%c %-*s %-*s -> %s  (%s)\",\n-\t\t\t    r ? '!' : '+',\n-\t\t\t    TRANSPORT_SUMMARY_WIDTH, quickref.buf,\n-\t\t\t    REFCOL_WIDTH, remote, pretty_ref,\n-\t\t\t    r ? _(\"unable to update local ref\") : _(\"forced update\"));\n+\t\tformat_display(display, r ? '!' : '+', quickref.buf,\n+\t\t\t       r ? _(\"unable to update local ref\") : _(\"forced update\"),\n+\t\t\t       remote, pretty_ref);\n \t\tstrbuf_release(&quickref);\n \t\treturn r;\n \t} else {\n-\t\tstrbuf_addf(display, \"! %-*s %-*s -> %s  %s\",\n-\t\t\t    TRANSPORT_SUMMARY(_(\"[rejected]\")),\n-\t\t\t    REFCOL_WIDTH, remote, pretty_ref,\n-\t\t\t    _(\"(non-fast-forward)\"));\n+\t\tformat_display(display, '!', _(\"[rejected]\"), _(\"non-fast-forward\"),\n+\t\t\t       remote, pretty_ref);\n \t\treturn 1;\n \t}\n }\n@@ -714,11 +712,10 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,\n \t\t\t\trc |= update_local_ref(ref, what, rm, &note);\n \t\t\t\tfree(ref);\n \t\t\t} else\n-\t\t\t\tstrbuf_addf(&note, \"* %-*s %-*s -> FETCH_HEAD\",\n-\t\t\t\t\t    TRANSPORT_SUMMARY_WIDTH,\n-\t\t\t\t\t    *kind ? kind : \"branch\",\n-\t\t\t\t\t    REFCOL_WIDTH,\n-\t\t\t\t\t    *what ? what : \"HEAD\");\n+\t\t\t\tformat_display(&note, '*',\n+\t\t\t\t\t       *kind ? kind : \"branch\", NULL,\n+\t\t\t\t\t       *what ? what : \"HEAD\",\n+\t\t\t\t\t       \"FETCH_HEAD\");\n \t\t\tif (note.len) {\n \t\t\t\tif (verbosity >= 0 && !shown_url) {\n \t\t\t\t\tfprintf(stderr, _(\"From %.*s\\n\"),\n@@ -812,13 +809,15 @@ static int prune_refs(struct refspec *refs, int ref_count, struct ref *ref_map,\n \n \tif (verbosity >= 0) {\n \t\tfor (ref = stale_refs; ref; ref = ref->next) {\n+\t\t\tstruct strbuf sb = STRBUF_INIT;\n \t\t\tif (!shown_url) {\n \t\t\t\tfprintf(stderr, _(\"From %.*s\\n\"), url_len, url);\n \t\t\t\tshown_url = 1;\n \t\t\t}\n-\t\t\tfprintf(stderr, \" x %-*s %-*s -> %s\\n\",\n-\t\t\t\tTRANSPORT_SUMMARY(_(\"[deleted]\")),\n-\t\t\t\tREFCOL_WIDTH, _(\"(none)\"), prettify_refname(ref->name));\n+\t\t\tformat_display(&sb, 'x', _(\"[deleted]\"), NULL,\n+\t\t\t\t       _(\"(none)\"), prettify_refname(ref->name));\n+\t\t\tfprintf(stderr, \" %s\\n\",sb.buf);\n+\t\t\tstrbuf_release(&sb);\n \t\t\twarn_dangling_symref(stderr, dangling_msg, ref->name);\n \t\t}\n \t}\n-- \n2.8.2.524.g6ff3d78\n"},{"id":"288375","messageId":"20160605031141.23513-4-pclouds@gmail.com","threadId":"42413","inReplyTo":"20160605031141.23513-1-pclouds@gmail.com","subject":"[PATCH v3 3/6] fetch: change flag code for displaying tag update and deleted ref","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-06-05T03:11:38Z","receivedAt":"2016-06-05T03:11:38Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This makes the fetch flag code consistent with push, where '-' means\ndeleted ref.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-fetch.txt | 4 ++--\n builtin/fetch.c             | 4 ++--\n 2 files changed, 4 insertions(+), 4 deletions(-)\n\ndiff --git a/Documentation/git-fetch.txt b/Documentation/git-fetch.txt\nindex a0d0539..6c6b2c1 100644\n--- a/Documentation/git-fetch.txt\n+++ b/Documentation/git-fetch.txt\n@@ -120,8 +120,8 @@ flag::\n \tA single character indicating the status of the ref:\n (space);; for a successfully fetched fast-forward;\n `+`;; for a successful forced update;\n-`x`;; for a successfully deleted ref;\n-`-`;; for a successful tag update;\n+`-`;; for a successfully deleted ref;\n+`t`;; for a successful tag update;\n `*`;; for a successfully fetched new ref;\n `!`;; for a ref that was rejected or failed to update; and\n `=`;; for a ref that was up to date and did not need fetching.\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex a7f152a..8177f90 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -500,7 +500,7 @@ static int update_local_ref(struct ref *ref,\n \t    starts_with(ref->name, \"refs/tags/\")) {\n \t\tint r;\n \t\tr = s_update_ref(\"updating tag\", ref, 0);\n-\t\tformat_display(display, r ? '!' : '-', _(\"[tag update]\"),\n+\t\tformat_display(display, r ? '!' : 't', _(\"[tag update]\"),\n \t\t\t       r ? _(\"unable to update local ref\") : NULL,\n \t\t\t       remote, pretty_ref);\n \t\treturn r;\n@@ -814,7 +814,7 @@ static int prune_refs(struct refspec *refs, int ref_count, struct ref *ref_map,\n \t\t\t\tfprintf(stderr, _(\"From %.*s\\n\"), url_len, url);\n \t\t\t\tshown_url = 1;\n \t\t\t}\n-\t\t\tformat_display(&sb, 'x', _(\"[deleted]\"), NULL,\n+\t\t\tformat_display(&sb, '-', _(\"[deleted]\"), NULL,\n \t\t\t\t       _(\"(none)\"), prettify_refname(ref->name));\n \t\t\tfprintf(stderr, \" %s\\n\",sb.buf);\n \t\t\tstrbuf_release(&sb);\n-- \n2.8.2.524.g6ff3d78\n"},{"id":"288376","messageId":"20160605031141.23513-5-pclouds@gmail.com","threadId":"42413","inReplyTo":"20160605031141.23513-1-pclouds@gmail.com","subject":"[PATCH v3 4/6] fetch: align all \"remote -> local\" output","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-06-05T03:11:39Z","receivedAt":"2016-06-05T03:11:39Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"We do align \"remote -> local\" output by allocating 10 columns to\n\"remote\". That produces aligned output only for short refs. An extra\npass is performed to find the longest remote ref name (that does not\nproduce a line longer than terminal width) to produce better aligned\noutput.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/fetch.c | 46 ++++++++++++++++++++++++++++++++++++++++++++--\n 1 file changed, 44 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 8177f90..c42795b 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -15,6 +15,7 @@\n #include \"submodule.h\"\n #include \"connected.h\"\n #include \"argv-array.h\"\n+#include \"utf8.h\"\n \n static const char * const builtin_fetch_usage[] = {\n \tN_(\"git fetch [<options>] [<repository> [<refspec>...]]\"),\n@@ -449,14 +450,53 @@ fail:\n \t\t\t   : STORE_REF_ERROR_OTHER;\n }\n \n-#define REFCOL_WIDTH  10\n+static int refcol_width = 10;\n+\n+static void adjust_refcol_width(const struct ref *ref)\n+{\n+\tint max, rlen, llen;\n+\n+\t/* uptodate lines are only shown on high verbosity level */\n+\tif (!verbosity && !oidcmp(&ref->peer_ref->old_oid, &ref->old_oid))\n+\t\treturn;\n+\n+\tmax    = term_columns();\n+\trlen   = utf8_strwidth(prettify_refname(ref->name));\n+\tllen   = utf8_strwidth(prettify_refname(ref->peer_ref->name));\n+\n+\t/*\n+\t * rough estimation to see if the output line is too long and\n+\t * should not be counted (we can't do precise calculation\n+\t * anyway because we don't know if the error explanation part\n+\t * will be printed in update_local_ref)\n+\t */\n+\tif (21 /* flag and summary */ + rlen + 4 /* -> */ + llen >= max)\n+\t\treturn;\n+\n+\tif (refcol_width < rlen)\n+\t\trefcol_width = rlen;\n+}\n+\n+static void prepare_format_display(struct ref *ref_map)\n+{\n+\tstruct ref *rm;\n+\n+\tfor (rm = ref_map; rm; rm = rm->next) {\n+\t\tif (rm->status == REF_STATUS_REJECT_SHALLOW ||\n+\t\t    !rm->peer_ref ||\n+\t\t    !strcmp(rm->name, \"HEAD\"))\n+\t\t\tcontinue;\n+\n+\t\tadjust_refcol_width(rm);\n+\t}\n+}\n \n static void format_display(struct strbuf *display, char code,\n \t\t\t   const char *summary, const char *error,\n \t\t\t   const char *remote, const char *local)\n {\n \tstrbuf_addf(display, \"%c %-*s \", code, TRANSPORT_SUMMARY(summary));\n-\tstrbuf_addf(display, \"%-*s -> %s\", REFCOL_WIDTH, remote, local);\n+\tstrbuf_addf(display, \"%-*s -> %s\", refcol_width, remote, local);\n \tif (error)\n \t\tstrbuf_addf(display, \"  (%s)\", error);\n }\n@@ -618,6 +658,8 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,\n \t\tgoto abort;\n \t}\n \n+\tprepare_format_display(ref_map);\n+\n \t/*\n \t * We do a pass for each fetch_head_status type in their enum order, so\n \t * merged entries are written before not-for-merge. That lets readers\n-- \n2.8.2.524.g6ff3d78\n"},{"id":"288377","messageId":"20160605031141.23513-6-pclouds@gmail.com","threadId":"42413","inReplyTo":"20160605031141.23513-1-pclouds@gmail.com","subject":"[PATCH v3 5/6] fetch: reduce duplicate in ref update status lines with { -> }","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-06-05T03:11:40Z","receivedAt":"2016-06-05T03:11:40Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"Most of the time, ref update lines are\n\n    variable-long-name -> fixed-remote/variable-long-name\n\nWith fetch.output set to \"compact\" such a line will be shown as\n\n    { -> fixed-remote}/variable-long-name\n\nThis keeps the output aligned (because the variable part is always at\nthe end) and reduce duplication.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/config.txt    |  4 +++\n Documentation/git-fetch.txt |  8 ++++++\n builtin/fetch.c             | 67 ++++++++++++++++++++++++++++++++++++++++++++-\n 3 files changed, 78 insertions(+), 1 deletion(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 2e1b2e4..a59aa32 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -1220,6 +1220,10 @@ fetch.prune::\n \tIf true, fetch will automatically behave as if the `--prune`\n \toption was given on the command line.  See also `remote.<name>.prune`.\n \n+fetch.output::\n+\tValid values are \"compact\" or \"full\". See OUTPUT section in\n+\tlinkgit:git-fetch[1] for detail.\n+\n format.attach::\n \tEnable multipart/mixed attachments as the default for\n \t'format-patch'.  The value can also be a double quoted string\ndiff --git a/Documentation/git-fetch.txt b/Documentation/git-fetch.txt\nindex 6c6b2c1..856796b 100644\n--- a/Documentation/git-fetch.txt\n+++ b/Documentation/git-fetch.txt\n@@ -113,6 +113,14 @@ representing the status of a single ref. Each line is of the form:\n  <flag> <summary> <from> -> <to> (<reason>)\n -------------------------------\n \n+When configuration variable `fetch.output` contains `compact` and\n+`from` and `to` share a common suffix, the line could be displayed in\n+the form:\n+\n+-------------------------------\n+ <flag> <summary> {<from> -> <to>}<common-suffix> (<reason>)\n+-------------------------------\n+\n The status of up-to-date refs is shown only if --verbose option is\n used.\n \ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex c42795b..e2ca6bc 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -451,6 +451,7 @@ fail:\n }\n \n static int refcol_width = 10;\n+static int compact_format;\n \n static void adjust_refcol_width(const struct ref *ref)\n {\n@@ -480,6 +481,19 @@ static void adjust_refcol_width(const struct ref *ref)\n static void prepare_format_display(struct ref *ref_map)\n {\n \tstruct ref *rm;\n+\tconst char *format = \"full\";\n+\n+\tgit_config_get_string_const(\"fetch.output\", &format);\n+\tif (!strcasecmp(format, \"full\"))\n+\t\tcompact_format = 0;\n+\telse if (!strcasecmp(format, \"compact\"))\n+\t\tcompact_format = 1;\n+\telse\n+\t\tdie(_(\"configuration fetch.output contains invalid value %s\"),\n+\t\t    format);\n+\n+\tif (compact_format)\n+\t\treturn;\n \n \tfor (rm = ref_map; rm; rm = rm->next) {\n \t\tif (rm->status == REF_STATUS_REJECT_SHALLOW ||\n@@ -491,12 +505,63 @@ static void prepare_format_display(struct ref *ref_map)\n \t}\n }\n \n+static int common_suffix_length(const char *a, const char *b)\n+{\n+\tconst char *pa = a + strlen(a);\n+\tconst char *pb = b + strlen(b);\n+\tint count = 0, final_count = 0;\n+\n+\twhile (pa > a && pb > b && pa[-1] == pb[-1]) {\n+\t\tpa--;\n+\t\tpb--;\n+\t\tcount++;\n+\t\tif (*pa == '/' ||\n+\t\t    /*\n+\t\t     * if a and b are \"abc\" and \"origin/abc\" respectively,\n+\t\t     * accept \"a\" as the suffix boundary too.\n+\t\t     */\n+\t\t    (pa == a && pb > b && pb[-1] == '/') ||\n+\t\t    (pb == b && pa > a && pa[-1] == '/'))\n+\t\t\tfinal_count = count;\n+\t}\n+\n+\treturn final_count;\n+}\n+\n+static void print_remote_to_local(struct strbuf *display,\n+\t\t\t\t  const char *remote, const char *local)\n+{\n+\tstrbuf_addf(display, \"%-*s -> %s\", refcol_width, remote, local);\n+}\n+\n+static void print_compact(struct strbuf *display,\n+\t\t\t  const char *remote, const char *local)\n+{\n+\tint len = common_suffix_length(remote, local);\n+\n+\tif (len) {\n+\t\tstrbuf_addf(display, \"{%.*s -> %.*s}%s\",\n+\t\t\t    (int)strlen(remote) - len, remote,\n+\t\t\t    (int)strlen(local) - len, local,\n+\t\t\t    remote + strlen(remote) - len);\n+\t\treturn;\n+\t}\n+\tprint_remote_to_local(display, remote, local);\n+}\n+\n static void format_display(struct strbuf *display, char code,\n \t\t\t   const char *summary, const char *error,\n \t\t\t   const char *remote, const char *local)\n {\n \tstrbuf_addf(display, \"%c %-*s \", code, TRANSPORT_SUMMARY(summary));\n-\tstrbuf_addf(display, \"%-*s -> %s\", refcol_width, remote, local);\n+\tswitch (compact_format) {\n+\tcase 0:\n+\t\tprint_remote_to_local(display, remote, local);\n+\t\tbreak;\n+\tcase 1:\n+\t\tprint_compact(display, remote, local);\n+\t\tbreak;\n+\t}\n \tif (error)\n \t\tstrbuf_addf(display, \"  (%s)\", error);\n }\n-- \n2.8.2.524.g6ff3d78\n"},{"id":"288378","messageId":"20160605031141.23513-7-pclouds@gmail.com","threadId":"42413","inReplyTo":"20160605031141.23513-1-pclouds@gmail.com","subject":"[PATCH v3 6/6] fetch: reduce duplicate in ref update status lines with placeholder","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-06-05T03:11:41Z","receivedAt":"2016-06-05T03:11:41Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"In the \"remote -> local\" line, if either ref is a substring of the\nother, the common part in the other string is replaced with \"$\". For\nexample\n\n    abc                -> origin/abc\n    refs/pull/123/head -> pull/123\n\nbecome\n\n    abc         -> origin/$\n    refs/$/head -> pull/123\n\nActivated with fetch.format=dollar.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/fetch.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++++-\n 1 file changed, 48 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex e2ca6bc..c63f913 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -488,11 +488,13 @@ static void prepare_format_display(struct ref *ref_map)\n \t\tcompact_format = 0;\n \telse if (!strcasecmp(format, \"compact\"))\n \t\tcompact_format = 1;\n+\telse if (!strcasecmp(format, \"dollar\"))\n+\t\tcompact_format = 2;\n \telse\n \t\tdie(_(\"configuration fetch.output contains invalid value %s\"),\n \t\t    format);\n \n-\tif (compact_format)\n+\tif (compact_format == 1)\n \t\treturn;\n \n \tfor (rm = ref_map; rm; rm = rm->next) {\n@@ -549,6 +551,48 @@ static void print_compact(struct strbuf *display,\n \tprint_remote_to_local(display, remote, local);\n }\n \n+static int dollarize(struct strbuf *haystack, const char *needle)\n+{\n+\tconst char *p = strstr(haystack->buf, needle);\n+\tint plen, nlen;\n+\n+\tif (!p)\n+\t\treturn 0;\n+\n+\tif (p > haystack->buf && p[-1] != '/')\n+\t\treturn 0;\n+\n+\tplen = strlen(p);\n+\tnlen = strlen(needle);\n+\tif (plen > nlen && p[nlen] != '/')\n+\t\treturn 0;\n+\n+\tstrbuf_splice(haystack, p - haystack->buf, nlen, \"$\", 1);\n+\treturn 1;\n+}\n+\n+static void print_dollar(struct strbuf *display,\n+\t\t\t const char *remote, const char *local)\n+{\n+\tstruct strbuf r = STRBUF_INIT;\n+\tstruct strbuf l = STRBUF_INIT;\n+\n+\tif (!strcmp(remote, local)) {\n+\t\tstrbuf_addf(display, \"%s -> $\", remote);\n+\t\treturn;\n+\t}\n+\n+\tstrbuf_addstr(&r, remote);\n+\tstrbuf_addstr(&l, local);\n+\n+\tif (!dollarize(&r, local))\n+\t\tdollarize(&l, remote);\n+\tprint_remote_to_local(display, r.buf, l.buf);\n+\n+\tstrbuf_release(&r);\n+\tstrbuf_release(&l);\n+}\n+\n static void format_display(struct strbuf *display, char code,\n \t\t\t   const char *summary, const char *error,\n \t\t\t   const char *remote, const char *local)\n@@ -561,6 +605,9 @@ static void format_display(struct strbuf *display, char code,\n \tcase 1:\n \t\tprint_compact(display, remote, local);\n \t\tbreak;\n+\tcase 2:\n+\t\tprint_dollar(display, remote, local);\n+\t\tbreak;\n \t}\n \tif (error)\n \t\tstrbuf_addf(display, \"  (%s)\", error);\n-- \n2.8.2.524.g6ff3d78\n"},{"id":"288379","messageId":"CACsJy8CmgqoWLdPzAWbWSu=65keywd0ni9mAdXUE7UDqxtJ3Vg@mail.gmail.com","threadId":"42413","inReplyTo":"xmqqeg8czzl4.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v2 3/3] fetch: reduce duplicate in ref update status lines","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2016-06-05T03:15:01Z","receivedAt":"2016-06-05T03:15:01Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Sat, Jun 4, 2016 at 11:30 PM, Junio C Hamano <gitster@pobox.com> wrote:\n>> The patch does not do fancy stuff like this yet, but it can because\n>> lines exceeding terminal width is already excluded from column width\n>> calculation. So far the output looks good on my terminal (192 chars,\n>> can't overflow or refnames are insanely long)\n>\n> Sounds like a sensible approach....\n>\n>> -- 8< --\n>> diff --git a/builtin/fetch.c b/builtin/fetch.c\n>> index a7f152a..5e1e5c9 100644\n>> --- a/builtin/fetch.c\n>> +++ b/builtin/fetch.c\n>> @@ -15,6 +15,7 @@\n>>  #include \"submodule.h\"\n>>  #include \"connected.h\"\n>>  #include \"argv-array.h\"\n>> +#include \"utf8.h\"\n>>\n>>  static const char * const builtin_fetch_usage[] = {\n>>       N_(\"git fetch [<options>] [<repository> [<refspec>...]]\"),\n>> @@ -449,14 +450,26 @@ fail:\n>>                          : STORE_REF_ERROR_OTHER;\n>>  }\n>>\n>> -#define REFCOL_WIDTH  10\n>> +static int refcol_width = 10;\n>> +\n>> +static void adjust_refcol_width(const char *remote, const char *local)\n>> +{\n>> +     int max = term_columns();\n>> +     int rlen = utf8_strwidth(remote);\n>> +     int llen = utf8_strwidth(local);\n>> +\n>> +     if (21 /* flag summary */ + rlen + 4 /* => */ + llen >= max)\n>> +             return;\n>> +     if (refcol_width < rlen)\n>> +             refcol_width = rlen;\n>> +}\n>>\n>>  static void format_display(struct strbuf *display, char code,\n>>                          const char *summary, const char *error,\n>>                          const char *remote, const char *local)\n>>  {\n>>       strbuf_addf(display, \"%c %-*s \", code, TRANSPORT_SUMMARY(summary));\n>> -     strbuf_addf(display, \"%-*s -> %s\", REFCOL_WIDTH, remote, local);\n>> +     strbuf_addf(display, \"%-*s -> %s\", refcol_width, remote, local);\n>\n> ... and if I understand correctly, this is the only place where you\n> need to decide if you need to switch to two lines, right?  You would\n> measure width of the remote and compare it with refcol_width or\n> something like that.\n\nExactly.\n-- \nDuy\n"},{"id":"288435","messageId":"5755879C.3030602@xiplink.com","threadId":"42413","inReplyTo":"20160605031141.23513-2-pclouds@gmail.com","subject":"Re: [PATCH v3 1/6] git-fetch.txt: document fetch output","fromName":"Marc Branchaud","fromEmail":"marcnarc@xiplink.com","sentAt":"2016-06-06T14:24:28Z","receivedAt":"2016-06-06T14:24:28Z","isPatch":true,"sender":{"key":"marcnarc@xiplink.com","avatar":"https://avatars.githubusercontent.com/u/14980203?v=4"},"body":"On 2016-06-04 11:11 PM, Nguyễn Thái Ngọc Duy wrote:\n> This documents the ref update status of fetch. The structure of this\n> output is defined in [1]. The ouput content is refined a bit in [2]\n> [3] [4].\n>\n> This patch is a copy from git-push.txt, modified a bit because the\n> flag '-' means different things in push (delete) and fetch (tag\n> update).\n>\n> PS. For code archaeologists, the discussion mentioned in [1] is\n> probably [5].\n>\n> [1] 165f390 (git-fetch: more terse fetch output - 2007-11-03)\n> [2] 6315472 (fetch: report local storage errors ... - 2008-06-26)\n> [3] f360d84 (builtin-fetch: add --prune option - 2009-11-10)\n> [4] 0997ada (fetch: describe new refs based on where... - 2012-04-16)\n> [5] http://thread.gmane.org/gmane.comp.version-control.git/61657\n>\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n>   Documentation/git-fetch.txt | 46 +++++++++++++++++++++++++++++++++++++++++++++\n>   1 file changed, 46 insertions(+)\n>\n> diff --git a/Documentation/git-fetch.txt b/Documentation/git-fetch.txt\n> index efe56e0..a0d0539 100644\n> --- a/Documentation/git-fetch.txt\n> +++ b/Documentation/git-fetch.txt\n> @@ -99,6 +99,52 @@ The latter use of the `remote.<repository>.fetch` values can be\n>   overridden by giving the `--refmap=<refspec>` parameter(s) on the\n>   command line.\n>\n> +OUTPUT\n> +------\n> +\n> +The output of \"git fetch\" depends on the transport method used; this\n> +section describes the output when fetch over the Git protocol (either\n\ns/fetch/fetching/\n\n> +locally or via ssh).\n> +\n> +The status of the fetch is output in tabular form, with each line\n> +representing the status of a single ref. Each line is of the form:\n> +\n> +-------------------------------\n> + <flag> <summary> <from> -> <to> (<reason>)\n> +-------------------------------\n\n(<reason>) should really just be <reason>, as the () are part of the \nreason string.\n\n> +\n> +The status of up-to-date refs is shown only if --verbose option is\n\ns/if/if the/\n\n\nAlso, thanks for putting so much effort into this!\n\nI think having a fetch.output configuration setting is perfectly fine. \nThis sort of thing is really a matter of personal taste, so having \nchoices is good.\n\n\t\tM.\n"},{"id":"290174","messageId":"20160626055810.26960-1-pclouds@gmail.com","threadId":"42413","inReplyTo":"20160605031141.23513-1-pclouds@gmail.com","subject":"[PATCH v4 0/5] Better ref summary alignment in \"git fetch\"","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-06-26T05:58:05Z","receivedAt":"2016-06-26T05:58:30Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"v4 is a cleaned up version of v3. Tests are added. Typos in\ngit-fetch.txt are corrected. The \"{ -> origin/}master\" format is\ndropped.\n\nNguyễn Thái Ngọc Duy (5):\n  git-fetch.txt: document fetch output\n  fetch: refactor ref update status formatting code\n  fetch: change flag code for displaying tag update and deleted ref\n  fetch: align all \"remote -> local\" output\n  fetch: reduce duplicate in ref update status lines with placeholder\n\n Documentation/config.txt    |   5 ++\n Documentation/git-fetch.txt |  51 ++++++++++++\n builtin/fetch.c             | 194 +++++++++++++++++++++++++++++++++++---------\n t/t5510-fetch.sh            |  30 +++++++\n 4 files changed, 240 insertions(+), 40 deletions(-)\n-- \n2.8.2.526.g02eed6d\n\n"},{"id":"290175","messageId":"20160626055810.26960-2-pclouds@gmail.com","threadId":"42413","inReplyTo":"20160626055810.26960-1-pclouds@gmail.com","subject":"[PATCH v4 1/5] git-fetch.txt: document fetch output","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-06-26T05:58:06Z","receivedAt":"2016-06-26T05:58:34Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This documents the ref update status of fetch. The structure of this\noutput is defined in [1]. The ouput content is refined a bit in [2]\n[3] [4].\n\nThis patch is a copy from git-push.txt, modified a bit because the\nflag '-' means different things in push (delete) and fetch (tag\nupdate).\n\nPS. For code archaeologists, the discussion mentioned in [1] is\nprobably [5].\n\n[1] 165f390 (git-fetch: more terse fetch output - 2007-11-03)\n[2] 6315472 (fetch: report local storage errors ... - 2008-06-26)\n[3] f360d84 (builtin-fetch: add --prune option - 2009-11-10)\n[4] 0997ada (fetch: describe new refs based on where... - 2012-04-16)\n[5] http://thread.gmane.org/gmane.comp.version-control.git/61657\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-fetch.txt | 46 +++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 46 insertions(+)\n\ndiff --git a/Documentation/git-fetch.txt b/Documentation/git-fetch.txt\nindex efe56e0..cbf441f 100644\n--- a/Documentation/git-fetch.txt\n+++ b/Documentation/git-fetch.txt\n@@ -99,6 +99,52 @@ The latter use of the `remote.<repository>.fetch` values can be\n overridden by giving the `--refmap=<refspec>` parameter(s) on the\n command line.\n \n+OUTPUT\n+------\n+\n+The output of \"git fetch\" depends on the transport method used; this\n+section describes the output when fetching over the Git protocol\n+(either locally or via ssh) and Smart HTTP protocol.\n+\n+The status of the fetch is output in tabular form, with each line\n+representing the status of a single ref. Each line is of the form:\n+\n+-------------------------------\n+ <flag> <summary> <from> -> <to> [<reason>]\n+-------------------------------\n+\n+The status of up-to-date refs is shown only if the --verbose option is\n+used.\n+\n+flag::\n+\tA single character indicating the status of the ref:\n+(space);; for a successfully fetched fast-forward;\n+`+`;; for a successful forced update;\n+`x`;; for a successfully pruned ref;\n+`-`;; for a successful tag update;\n+`*`;; for a successfully fetched new ref;\n+`!`;; for a ref that was rejected or failed to update; and\n+`=`;; for a ref that was up to date and did not need fetching.\n+\n+summary::\n+\tFor a successfully fetched ref, the summary shows the old and new\n+\tvalues of the ref in a form suitable for using as an argument to\n+\t`git log` (this is `<old>..<new>` in most cases, and\n+\t`<old>...<new>` for forced non-fast-forward updates).\n+\n+from::\n+\tThe name of the remote ref being fetched from, minus its\n+\t`refs/<type>/` prefix. In the case of deletion, the name of\n+\tthe remote ref is \"(none)\".\n+\n+to::\n+\tThe name of the local ref being updated, minus its\n+\t`refs/<type>/` prefix.\n+\n+reason::\n+\tA human-readable explanation. In the case of successfully fetched\n+\trefs, no explanation is needed. For a failed ref, the reason for\n+\tfailure is described.\n \n EXAMPLES\n --------\n-- \n2.8.2.526.g02eed6d\n\n"},{"id":"290176","messageId":"20160626055810.26960-3-pclouds@gmail.com","threadId":"42413","inReplyTo":"20160626055810.26960-1-pclouds@gmail.com","subject":"[PATCH v4 2/5] fetch: refactor ref update status formatting code","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-06-26T05:58:07Z","receivedAt":"2016-06-26T05:58:37Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This makes it easier to change the formatting later. And it makes sure\ntranslators cannot mess up format specifiers and break Git.\n\nThere are a couple call sites where the length of the second column is\nTRANSPORT_SUMMARY_WIDTH instead of calculated by TRANSPORT_SUMMARY(),\nwhich is enforced now. The result should be the same because these call\nsites do not contain characters outside ASCII range.\n\nThe two strbuf_addf() calls instead of one is mostly to reduce\ndiff-noise in a future patch where \"ref -> ref\" is reformatted\ndifferently.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/fetch.c | 77 ++++++++++++++++++++++++++++-----------------------------\n 1 file changed, 38 insertions(+), 39 deletions(-)\n\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 1582ca7..a7f152a 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -451,6 +451,16 @@ fail:\n \n #define REFCOL_WIDTH  10\n \n+static void format_display(struct strbuf *display, char code,\n+\t\t\t   const char *summary, const char *error,\n+\t\t\t   const char *remote, const char *local)\n+{\n+\tstrbuf_addf(display, \"%c %-*s \", code, TRANSPORT_SUMMARY(summary));\n+\tstrbuf_addf(display, \"%-*s -> %s\", REFCOL_WIDTH, remote, local);\n+\tif (error)\n+\t\tstrbuf_addf(display, \"  (%s)\", error);\n+}\n+\n static int update_local_ref(struct ref *ref,\n \t\t\t    const char *remote,\n \t\t\t    const struct ref *remote_ref,\n@@ -467,9 +477,8 @@ static int update_local_ref(struct ref *ref,\n \n \tif (!oidcmp(&ref->old_oid, &ref->new_oid)) {\n \t\tif (verbosity > 0)\n-\t\t\tstrbuf_addf(display, \"= %-*s %-*s -> %s\",\n-\t\t\t\t    TRANSPORT_SUMMARY(_(\"[up to date]\")),\n-\t\t\t\t    REFCOL_WIDTH, remote, pretty_ref);\n+\t\t\tformat_display(display, '=', _(\"[up to date]\"), NULL,\n+\t\t\t\t       remote, pretty_ref);\n \t\treturn 0;\n \t}\n \n@@ -481,10 +490,9 @@ static int update_local_ref(struct ref *ref,\n \t\t * If this is the head, and it's not okay to update\n \t\t * the head, and the old value of the head isn't empty...\n \t\t */\n-\t\tstrbuf_addf(display,\n-\t\t\t    _(\"! %-*s %-*s -> %s  (can't fetch in current branch)\"),\n-\t\t\t    TRANSPORT_SUMMARY(_(\"[rejected]\")),\n-\t\t\t    REFCOL_WIDTH, remote, pretty_ref);\n+\t\tformat_display(display, '!', _(\"[rejected]\"),\n+\t\t\t       _(\"can't fetch in current branch\"),\n+\t\t\t       remote, pretty_ref);\n \t\treturn 1;\n \t}\n \n@@ -492,11 +500,9 @@ static int update_local_ref(struct ref *ref,\n \t    starts_with(ref->name, \"refs/tags/\")) {\n \t\tint r;\n \t\tr = s_update_ref(\"updating tag\", ref, 0);\n-\t\tstrbuf_addf(display, \"%c %-*s %-*s -> %s%s\",\n-\t\t\t    r ? '!' : '-',\n-\t\t\t    TRANSPORT_SUMMARY(_(\"[tag update]\")),\n-\t\t\t    REFCOL_WIDTH, remote, pretty_ref,\n-\t\t\t    r ? _(\"  (unable to update local ref)\") : \"\");\n+\t\tformat_display(display, r ? '!' : '-', _(\"[tag update]\"),\n+\t\t\t       r ? _(\"unable to update local ref\") : NULL,\n+\t\t\t       remote, pretty_ref);\n \t\treturn r;\n \t}\n \n@@ -527,11 +533,9 @@ static int update_local_ref(struct ref *ref,\n \t\t    (recurse_submodules != RECURSE_SUBMODULES_ON))\n \t\t\tcheck_for_new_submodule_commits(ref->new_oid.hash);\n \t\tr = s_update_ref(msg, ref, 0);\n-\t\tstrbuf_addf(display, \"%c %-*s %-*s -> %s%s\",\n-\t\t\t    r ? '!' : '*',\n-\t\t\t    TRANSPORT_SUMMARY(what),\n-\t\t\t    REFCOL_WIDTH, remote, pretty_ref,\n-\t\t\t    r ? _(\"  (unable to update local ref)\") : \"\");\n+\t\tformat_display(display, r ? '!' : '*', what,\n+\t\t\t       r ? _(\"unable to update local ref\") : NULL,\n+\t\t\t       remote, pretty_ref);\n \t\treturn r;\n \t}\n \n@@ -545,11 +549,9 @@ static int update_local_ref(struct ref *ref,\n \t\t    (recurse_submodules != RECURSE_SUBMODULES_ON))\n \t\t\tcheck_for_new_submodule_commits(ref->new_oid.hash);\n \t\tr = s_update_ref(\"fast-forward\", ref, 1);\n-\t\tstrbuf_addf(display, \"%c %-*s %-*s -> %s%s\",\n-\t\t\t    r ? '!' : ' ',\n-\t\t\t    TRANSPORT_SUMMARY_WIDTH, quickref.buf,\n-\t\t\t    REFCOL_WIDTH, remote, pretty_ref,\n-\t\t\t    r ? _(\"  (unable to update local ref)\") : \"\");\n+\t\tformat_display(display, r ? '!' : ' ', quickref.buf,\n+\t\t\t       r ? _(\"unable to update local ref\") : NULL,\n+\t\t\t       remote, pretty_ref);\n \t\tstrbuf_release(&quickref);\n \t\treturn r;\n \t} else if (force || ref->force) {\n@@ -562,18 +564,14 @@ static int update_local_ref(struct ref *ref,\n \t\t    (recurse_submodules != RECURSE_SUBMODULES_ON))\n \t\t\tcheck_for_new_submodule_commits(ref->new_oid.hash);\n \t\tr = s_update_ref(\"forced-update\", ref, 1);\n-\t\tstrbuf_addf(display, \"%c %-*s %-*s -> %s  (%s)\",\n-\t\t\t    r ? '!' : '+',\n-\t\t\t    TRANSPORT_SUMMARY_WIDTH, quickref.buf,\n-\t\t\t    REFCOL_WIDTH, remote, pretty_ref,\n-\t\t\t    r ? _(\"unable to update local ref\") : _(\"forced update\"));\n+\t\tformat_display(display, r ? '!' : '+', quickref.buf,\n+\t\t\t       r ? _(\"unable to update local ref\") : _(\"forced update\"),\n+\t\t\t       remote, pretty_ref);\n \t\tstrbuf_release(&quickref);\n \t\treturn r;\n \t} else {\n-\t\tstrbuf_addf(display, \"! %-*s %-*s -> %s  %s\",\n-\t\t\t    TRANSPORT_SUMMARY(_(\"[rejected]\")),\n-\t\t\t    REFCOL_WIDTH, remote, pretty_ref,\n-\t\t\t    _(\"(non-fast-forward)\"));\n+\t\tformat_display(display, '!', _(\"[rejected]\"), _(\"non-fast-forward\"),\n+\t\t\t       remote, pretty_ref);\n \t\treturn 1;\n \t}\n }\n@@ -714,11 +712,10 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,\n \t\t\t\trc |= update_local_ref(ref, what, rm, &note);\n \t\t\t\tfree(ref);\n \t\t\t} else\n-\t\t\t\tstrbuf_addf(&note, \"* %-*s %-*s -> FETCH_HEAD\",\n-\t\t\t\t\t    TRANSPORT_SUMMARY_WIDTH,\n-\t\t\t\t\t    *kind ? kind : \"branch\",\n-\t\t\t\t\t    REFCOL_WIDTH,\n-\t\t\t\t\t    *what ? what : \"HEAD\");\n+\t\t\t\tformat_display(&note, '*',\n+\t\t\t\t\t       *kind ? kind : \"branch\", NULL,\n+\t\t\t\t\t       *what ? what : \"HEAD\",\n+\t\t\t\t\t       \"FETCH_HEAD\");\n \t\t\tif (note.len) {\n \t\t\t\tif (verbosity >= 0 && !shown_url) {\n \t\t\t\t\tfprintf(stderr, _(\"From %.*s\\n\"),\n@@ -812,13 +809,15 @@ static int prune_refs(struct refspec *refs, int ref_count, struct ref *ref_map,\n \n \tif (verbosity >= 0) {\n \t\tfor (ref = stale_refs; ref; ref = ref->next) {\n+\t\t\tstruct strbuf sb = STRBUF_INIT;\n \t\t\tif (!shown_url) {\n \t\t\t\tfprintf(stderr, _(\"From %.*s\\n\"), url_len, url);\n \t\t\t\tshown_url = 1;\n \t\t\t}\n-\t\t\tfprintf(stderr, \" x %-*s %-*s -> %s\\n\",\n-\t\t\t\tTRANSPORT_SUMMARY(_(\"[deleted]\")),\n-\t\t\t\tREFCOL_WIDTH, _(\"(none)\"), prettify_refname(ref->name));\n+\t\t\tformat_display(&sb, 'x', _(\"[deleted]\"), NULL,\n+\t\t\t\t       _(\"(none)\"), prettify_refname(ref->name));\n+\t\t\tfprintf(stderr, \" %s\\n\",sb.buf);\n+\t\t\tstrbuf_release(&sb);\n \t\t\twarn_dangling_symref(stderr, dangling_msg, ref->name);\n \t\t}\n \t}\n-- \n2.8.2.526.g02eed6d\n\n"},{"id":"290177","messageId":"20160626055810.26960-4-pclouds@gmail.com","threadId":"42413","inReplyTo":"20160626055810.26960-1-pclouds@gmail.com","subject":"[PATCH v4 3/5] fetch: change flag code for displaying tag update and deleted ref","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-06-26T05:58:08Z","receivedAt":"2016-06-26T05:58:40Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This makes the fetch flag code consistent with push, where '-' means\ndeleted ref.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-fetch.txt | 4 ++--\n builtin/fetch.c             | 4 ++--\n 2 files changed, 4 insertions(+), 4 deletions(-)\n\ndiff --git a/Documentation/git-fetch.txt b/Documentation/git-fetch.txt\nindex cbf441f..771dde5 100644\n--- a/Documentation/git-fetch.txt\n+++ b/Documentation/git-fetch.txt\n@@ -120,8 +120,8 @@ flag::\n \tA single character indicating the status of the ref:\n (space);; for a successfully fetched fast-forward;\n `+`;; for a successful forced update;\n-`x`;; for a successfully pruned ref;\n-`-`;; for a successful tag update;\n+`-`;; for a successfully pruned ref;\n+`t`;; for a successful tag update;\n `*`;; for a successfully fetched new ref;\n `!`;; for a ref that was rejected or failed to update; and\n `=`;; for a ref that was up to date and did not need fetching.\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex a7f152a..8177f90 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -500,7 +500,7 @@ static int update_local_ref(struct ref *ref,\n \t    starts_with(ref->name, \"refs/tags/\")) {\n \t\tint r;\n \t\tr = s_update_ref(\"updating tag\", ref, 0);\n-\t\tformat_display(display, r ? '!' : '-', _(\"[tag update]\"),\n+\t\tformat_display(display, r ? '!' : 't', _(\"[tag update]\"),\n \t\t\t       r ? _(\"unable to update local ref\") : NULL,\n \t\t\t       remote, pretty_ref);\n \t\treturn r;\n@@ -814,7 +814,7 @@ static int prune_refs(struct refspec *refs, int ref_count, struct ref *ref_map,\n \t\t\t\tfprintf(stderr, _(\"From %.*s\\n\"), url_len, url);\n \t\t\t\tshown_url = 1;\n \t\t\t}\n-\t\t\tformat_display(&sb, 'x', _(\"[deleted]\"), NULL,\n+\t\t\tformat_display(&sb, '-', _(\"[deleted]\"), NULL,\n \t\t\t\t       _(\"(none)\"), prettify_refname(ref->name));\n \t\t\tfprintf(stderr, \" %s\\n\",sb.buf);\n \t\t\tstrbuf_release(&sb);\n-- \n2.8.2.526.g02eed6d\n\n"},{"id":"290178","messageId":"20160626055810.26960-5-pclouds@gmail.com","threadId":"42413","inReplyTo":"20160626055810.26960-1-pclouds@gmail.com","subject":"[PATCH v4 4/5] fetch: align all \"remote -> local\" output","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-06-26T05:58:09Z","receivedAt":"2016-06-26T05:58:41Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"We do align \"remote -> local\" output by allocating 10 columns to\n\"remote\". That produces aligned output only for short refs. An extra\npass is performed to find the longest remote ref name (that does not\nproduce a line longer than terminal width) to produce better aligned\noutput.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/fetch.c  | 46 ++++++++++++++++++++++++++++++++++++++++++++--\n t/t5510-fetch.sh | 15 +++++++++++++++\n 2 files changed, 59 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 8177f90..c42795b 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -15,6 +15,7 @@\n #include \"submodule.h\"\n #include \"connected.h\"\n #include \"argv-array.h\"\n+#include \"utf8.h\"\n \n static const char * const builtin_fetch_usage[] = {\n \tN_(\"git fetch [<options>] [<repository> [<refspec>...]]\"),\n@@ -449,14 +450,53 @@ fail:\n \t\t\t   : STORE_REF_ERROR_OTHER;\n }\n \n-#define REFCOL_WIDTH  10\n+static int refcol_width = 10;\n+\n+static void adjust_refcol_width(const struct ref *ref)\n+{\n+\tint max, rlen, llen;\n+\n+\t/* uptodate lines are only shown on high verbosity level */\n+\tif (!verbosity && !oidcmp(&ref->peer_ref->old_oid, &ref->old_oid))\n+\t\treturn;\n+\n+\tmax    = term_columns();\n+\trlen   = utf8_strwidth(prettify_refname(ref->name));\n+\tllen   = utf8_strwidth(prettify_refname(ref->peer_ref->name));\n+\n+\t/*\n+\t * rough estimation to see if the output line is too long and\n+\t * should not be counted (we can't do precise calculation\n+\t * anyway because we don't know if the error explanation part\n+\t * will be printed in update_local_ref)\n+\t */\n+\tif (21 /* flag and summary */ + rlen + 4 /* -> */ + llen >= max)\n+\t\treturn;\n+\n+\tif (refcol_width < rlen)\n+\t\trefcol_width = rlen;\n+}\n+\n+static void prepare_format_display(struct ref *ref_map)\n+{\n+\tstruct ref *rm;\n+\n+\tfor (rm = ref_map; rm; rm = rm->next) {\n+\t\tif (rm->status == REF_STATUS_REJECT_SHALLOW ||\n+\t\t    !rm->peer_ref ||\n+\t\t    !strcmp(rm->name, \"HEAD\"))\n+\t\t\tcontinue;\n+\n+\t\tadjust_refcol_width(rm);\n+\t}\n+}\n \n static void format_display(struct strbuf *display, char code,\n \t\t\t   const char *summary, const char *error,\n \t\t\t   const char *remote, const char *local)\n {\n \tstrbuf_addf(display, \"%c %-*s \", code, TRANSPORT_SUMMARY(summary));\n-\tstrbuf_addf(display, \"%-*s -> %s\", REFCOL_WIDTH, remote, local);\n+\tstrbuf_addf(display, \"%-*s -> %s\", refcol_width, remote, local);\n \tif (error)\n \t\tstrbuf_addf(display, \"  (%s)\", error);\n }\n@@ -618,6 +658,8 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,\n \t\tgoto abort;\n \t}\n \n+\tprepare_format_display(ref_map);\n+\n \t/*\n \t * We do a pass for each fetch_head_status type in their enum order, so\n \t * merged entries are written before not-for-merge. That lets readers\ndiff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\nindex 454d896..f50497e 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -688,4 +688,19 @@ test_expect_success 'fetching with auto-gc does not lock up' '\n \t)\n '\n \n+test_expect_success 'fetch aligned output' '\n+\tgit clone . full-output &&\n+\ttest_commit looooooooooooong-tag &&\n+\t(\n+\t\tcd full-output &&\n+\t\tgit fetch origin 2>&1 | \\\n+\t\t\tgrep -e \"->\" | cut -c 22- >../actual\n+\t) &&\n+\tcat >expect <<-\\EOF &&\n+\tmaster               -> origin/master\n+\tlooooooooooooong-tag -> looooooooooooong-tag\n+\tEOF\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n2.8.2.526.g02eed6d\n\n"},{"id":"290179","messageId":"20160626055810.26960-6-pclouds@gmail.com","threadId":"42413","inReplyTo":"20160626055810.26960-1-pclouds@gmail.com","subject":"[PATCH v4 5/5] fetch: reduce duplicate in ref update status lines with placeholder","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-06-26T05:58:10Z","receivedAt":"2016-06-26T05:58:42Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"In the \"remote -> local\" line, if either ref is a substring of the\nother, the common part in the other string is replaced with \"$\". For\nexample\n\n    abc                -> origin/abc\n    refs/pull/123/head -> pull/123\n\nbecome\n\n    abc         -> origin/$\n    refs/$/head -> pull/123\n\nActivated with fetch.output=compact.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/config.txt    |  5 +++\n Documentation/git-fetch.txt |  5 +++\n builtin/fetch.c             | 75 ++++++++++++++++++++++++++++++++++++++++++++-\n t/t5510-fetch.sh            | 17 +++++++++-\n 4 files changed, 100 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 2e1b2e4..7f6e58d 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -1220,6 +1220,11 @@ fetch.prune::\n \tIf true, fetch will automatically behave as if the `--prune`\n \toption was given on the command line.  See also `remote.<name>.prune`.\n \n+fetch.output::\n+\tControl how ref update status is printed. Valid values are\n+\t`full` and `compact`. Default value is `full`. See section\n+\tOUTPUT in linkgit:git-fetch[1] for detail.\n+\n format.attach::\n \tEnable multipart/mixed attachments as the default for\n \t'format-patch'.  The value can also be a double quoted string\ndiff --git a/Documentation/git-fetch.txt b/Documentation/git-fetch.txt\nindex 771dde5..61e8885 100644\n--- a/Documentation/git-fetch.txt\n+++ b/Documentation/git-fetch.txt\n@@ -116,6 +116,11 @@ representing the status of a single ref. Each line is of the form:\n The status of up-to-date refs is shown only if the --verbose option is\n used.\n \n+In compact output mode, if either entire `<from>` or `<to>` is found\n+in the other string, it will be substituted with `$` in the other\n+string. or example, `master -> origin/master` becomes\n+`master -> origin/$`.\n+\n flag::\n \tA single character indicating the status of the ref:\n (space);; for a successfully fetched fast-forward;\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex c42795b..9d9f4e8 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -451,6 +451,7 @@ fail:\n }\n \n static int refcol_width = 10;\n+static int compact_format;\n \n static void adjust_refcol_width(const struct ref *ref)\n {\n@@ -462,6 +463,17 @@ static void adjust_refcol_width(const struct ref *ref)\n \n \tmax    = term_columns();\n \trlen   = utf8_strwidth(prettify_refname(ref->name));\n+\n+\tif (compact_format) {\n+\t\t/*\n+\t\t * Not precise calculation because '$' can appear in\n+\t\t * ref->name and reduce actual length.\n+\t\t */\n+\t\tif (refcol_width < rlen)\n+\t\t\trefcol_width = rlen;\n+\t\treturn;\n+\t}\n+\n \tllen   = utf8_strwidth(prettify_refname(ref->peer_ref->name));\n \n \t/*\n@@ -480,6 +492,16 @@ static void adjust_refcol_width(const struct ref *ref)\n static void prepare_format_display(struct ref *ref_map)\n {\n \tstruct ref *rm;\n+\tconst char *format = \"full\";\n+\n+\tgit_config_get_string_const(\"fetch.output\", &format);\n+\tif (!strcasecmp(format, \"full\"))\n+\t\tcompact_format = 0;\n+\telse if (!strcasecmp(format, \"compact\"))\n+\t\tcompact_format = 1;\n+\telse\n+\t\tdie(_(\"configuration fetch.output contains invalid value %s\"),\n+\t\t    format);\n \n \tfor (rm = ref_map; rm; rm = rm->next) {\n \t\tif (rm->status == REF_STATUS_REJECT_SHALLOW ||\n@@ -491,12 +513,63 @@ static void prepare_format_display(struct ref *ref_map)\n \t}\n }\n \n+static void print_remote_to_local(struct strbuf *display,\n+\t\t\t\t  const char *remote, const char *local)\n+{\n+\tstrbuf_addf(display, \"%-*s -> %s\", refcol_width, remote, local);\n+}\n+\n+static int dollarize(struct strbuf *haystack, const char *needle)\n+{\n+\tconst char *p = strstr(haystack->buf, needle);\n+\tint plen, nlen;\n+\n+\tif (!p)\n+\t\treturn 0;\n+\n+\tif (p > haystack->buf && p[-1] != '/')\n+\t\treturn 0;\n+\n+\tplen = strlen(p);\n+\tnlen = strlen(needle);\n+\tif (plen > nlen && p[nlen] != '/')\n+\t\treturn 0;\n+\n+\tstrbuf_splice(haystack, p - haystack->buf, nlen, \"$\", 1);\n+\treturn 1;\n+}\n+\n+static void print_compact(struct strbuf *display,\n+\t\t\t  const char *remote, const char *local)\n+{\n+\tstruct strbuf r = STRBUF_INIT;\n+\tstruct strbuf l = STRBUF_INIT;\n+\n+\tif (!strcmp(remote, local)) {\n+\t\tstrbuf_addf(display, \"%-*s -> $\", refcol_width, remote);\n+\t\treturn;\n+\t}\n+\n+\tstrbuf_addstr(&r, remote);\n+\tstrbuf_addstr(&l, local);\n+\n+\tif (!dollarize(&r, local))\n+\t\tdollarize(&l, remote);\n+\tprint_remote_to_local(display, r.buf, l.buf);\n+\n+\tstrbuf_release(&r);\n+\tstrbuf_release(&l);\n+}\n+\n static void format_display(struct strbuf *display, char code,\n \t\t\t   const char *summary, const char *error,\n \t\t\t   const char *remote, const char *local)\n {\n \tstrbuf_addf(display, \"%c %-*s \", code, TRANSPORT_SUMMARY(summary));\n-\tstrbuf_addf(display, \"%-*s -> %s\", refcol_width, remote, local);\n+\tif (!compact_format)\n+\t\tprint_remote_to_local(display, remote, local);\n+\telse\n+\t\tprint_compact(display, remote, local);\n \tif (error)\n \t\tstrbuf_addf(display, \"  (%s)\", error);\n }\ndiff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\nindex f50497e..3a92718 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -693,7 +693,7 @@ test_expect_success 'fetch aligned output' '\n \ttest_commit looooooooooooong-tag &&\n \t(\n \t\tcd full-output &&\n-\t\tgit fetch origin 2>&1 | \\\n+\t\tgit -c fetch.output=full fetch origin 2>&1 | \\\n \t\t\tgrep -e \"->\" | cut -c 22- >../actual\n \t) &&\n \tcat >expect <<-\\EOF &&\n@@ -703,4 +703,19 @@ test_expect_success 'fetch aligned output' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'fetch compact output' '\n+\tgit clone . compact &&\n+\ttest_commit extraaa &&\n+\t(\n+\t\tcd compact &&\n+\t\tgit -c fetch.output=compact fetch origin 2>&1 | \\\n+\t\t\tgrep -e \"->\" | cut -c 22- >../actual\n+\t) &&\n+\tcat >expect <<-\\EOF &&\n+\tmaster     -> origin/$\n+\textraaa    -> $\n+\tEOF\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n2.8.2.526.g02eed6d\n\n"},{"id":"290216","messageId":"CAPig+cT=BAuAbEmS5G1v2yGNczA+-WvW0bBZz_hrLcYi0bpO4g@mail.gmail.com","threadId":"42413","inReplyTo":"20160626055810.26960-6-pclouds@gmail.com","subject":"Re: [PATCH v4 5/5] fetch: reduce duplicate in ref update status lines with placeholder","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2016-06-27T04:33:28Z","receivedAt":"2016-06-27T04:33:34Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Sun, Jun 26, 2016 at 1:58 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n> In the \"remote -> local\" line, if either ref is a substring of the\n> other, the common part in the other string is replaced with \"$\". For\n> example\n>\n>     abc                -> origin/abc\n>     refs/pull/123/head -> pull/123\n>\n> become\n>\n>     abc         -> origin/$\n>     refs/$/head -> pull/123\n\nBikeshedding...\n\nI think I recall in an earlier iteration that you asked for opinions\nabout '$', but don't recall if there were responses. Have you\nconsidered '*' rather than '$'?\n\nIn my brain, at least, '$' is associated so strongly with regex that\n\"origin/$\" is interpreted automatically as anchoring \"origin/\" at the\nend of string, and \"refs/$/head\" just feels weird.\n\nOn the other hand, given the familiarity of shell globbing, \"origin/*\"\nand \"refs/*/head\" feel quite natural and intuitive.\n\n> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n> ---\n> diff --git a/Documentation/git-fetch.txt b/Documentation/git-fetch.txt\n> @@ -116,6 +116,11 @@ representing the status of a single ref. Each line is of the form:\n> +In compact output mode, if either entire `<from>` or `<to>` is found\n> +in the other string, it will be substituted with `$` in the other\n> +string. or example, `master -> origin/master` becomes\n\ns/or/For/\n\n> +`master -> origin/$`.\n"},{"id":"290219","messageId":"CACsJy8Cq8Jp1yNWS-oBoZNTKqqsSZMM==Y3y1feXdutWSizo9w@mail.gmail.com","threadId":"42413","inReplyTo":"CAPig+cT=BAuAbEmS5G1v2yGNczA+-WvW0bBZz_hrLcYi0bpO4g@mail.gmail.com","subject":"Re: [PATCH v4 5/5] fetch: reduce duplicate in ref update status lines with placeholder","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2016-06-27T05:42:05Z","receivedAt":"2016-06-27T05:42:45Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Mon, Jun 27, 2016 at 6:33 AM, Eric Sunshine <sunshine@sunshineco.com> wrote:\n> On Sun, Jun 26, 2016 at 1:58 AM, Nguyễn Thái Ngọc Duy <pclouds@gmail.com> wrote:\n>> In the \"remote -> local\" line, if either ref is a substring of the\n>> other, the common part in the other string is replaced with \"$\". For\n>> example\n>>\n>>     abc                -> origin/abc\n>>     refs/pull/123/head -> pull/123\n>>\n>> become\n>>\n>>     abc         -> origin/$\n>>     refs/$/head -> pull/123\n>\n> Bikeshedding...\n>\n> I think I recall in an earlier iteration that you asked for opinions\n> about '$', but don't recall if there were responses. Have you\n> considered '*' rather than '$'?\n\nI did. But I remembered that * is used as wildcard in refspec, which\nis specified on both sides, e.g. refs/heads/*:refs/remotes/foo/* and\nwondered if it could cause a little confusion when the user sees '*'\nonly on one side here. But I have no strong opinion on '$' or '*' or\nany other character. So if nobody says anything else, the next re-roll\nwill go with '*'.\n\n> In my brain, at least, '$' is associated so strongly with regex that\n> \"origin/$\" is interpreted automatically as anchoring \"origin/\" at the\n> end of string, and \"refs/$/head\" just feels weird.\n\nOn the other hand, '$' has been used as the variable expansion symbol\nin shell, tcl, perl and php (which are probably the same thing since I\nhave a feeling all of them borrow '$' from one source, probably\nshell). But yeah, '$' at the end could remind people of regexp too.\n\n> On the other hand, given the familiarity of shell globbing, \"origin/*\"\n> and \"refs/*/head\" feel quite natural and intuitive.\n-- \nDuy\n"},{"id":"290239","messageId":"xmqqvb0ubqcw.fsf@gitster.mtv.corp.google.com","threadId":"42413","inReplyTo":"CAPig+cT=BAuAbEmS5G1v2yGNczA+-WvW0bBZz_hrLcYi0bpO4g@mail.gmail.com","subject":"Re: [PATCH v4 5/5] fetch: reduce duplicate in ref update status lines with placeholder","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-06-27T15:31:27Z","receivedAt":"2016-06-27T15:31:35Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Eric Sunshine <sunshine@sunshineco.com> writes:\n\n> In my brain, at least, '$' is associated so strongly with regex that\n> \"origin/$\" is interpreted automatically as anchoring \"origin/\" at the\n> end of string, and \"refs/$/head\" just feels weird.\n>\n> On the other hand, given the familiarity of shell globbing, \"origin/*\"\n> and \"refs/*/head\" feel quite natural and intuitive.\n\nI had the same thought ;-)\n"},{"id":"290310","messageId":"20160627184354.GA9594@sigill.intra.peff.net","threadId":"42413","inReplyTo":"20160626055810.26960-1-pclouds@gmail.com","subject":"Re: [PATCH v4 0/5] Better ref summary alignment in \"git fetch\"","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2016-06-27T18:43:54Z","receivedAt":"2016-06-27T18:44:02Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Sun, Jun 26, 2016 at 07:58:05AM +0200, Nguyễn Thái Ngọc Duy wrote:\n\n> v4 is a cleaned up version of v3. Tests are added. Typos in\n> git-fetch.txt are corrected. The \"{ -> origin/}master\" format is\n> dropped.\n\nThanks for continuing to look into this.\n\nI tried it on my most-horrible example case, and the results were...just\nOK. Because the variable-length part of each line comes first, the\nalignment code means that the \"origin/$\" bit of every line gets bumped\nout. And if you have a single large branch name, then everybody gets\nbumped out very far, even to the point of wrapping. E.g., I get\nsomething like (with fetch.output=compact, obviously):\n\n  From ...\n   * [new branch]      branch1                      -> origin/$\n   * [new branch]      branch2                      -> origin/$\n   * [new branch]      some-really-long-branch-name -> origin/$\n   + 1234abc..5678def  branch3                      -> origin/$ (forced\n    update)\n   * [new branch]      branch4                      -> origin/$\n\nI've shrunk it a bit to fit in the email; my actual \"long\" name was much\nlarger. And the average length for the shorter ones is, too, but the\noverall effect is the same; almost every line has a huge run of\nwhitespace. And some lines wrap that would not have even under the\nnormal, duplicated scheme.\n\nOne of the nice things about Junio's \"{ -> origin/}\" suggestion is that\nit puts the variable-length part at the end, so there's no extra\nalignment required. And you'd get something like:\n\n  From ...\n   * [new branch]      { -> origin/}branch1\n   * [new branch]      { -> origin/}branch2\n   * [new branch]      { -> origin/}some-really-long-branch-name\n   + 1234abc..5678def  { -> origin/}branch3 (forced update)\n   * [new branch]      { -> origin/}branch4\n\n-Peff\n"},{"id":"290316","messageId":"20160627192732.GA5774@duynguyen","threadId":"42413","inReplyTo":"20160627184354.GA9594@sigill.intra.peff.net","subject":"Re: [PATCH v4 0/5] Better ref summary alignment in \"git fetch\"","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2016-06-27T19:27:32Z","receivedAt":"2016-06-27T19:27:42Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Mon, Jun 27, 2016 at 02:43:54PM -0400, Jeff King wrote:\n> On Sun, Jun 26, 2016 at 07:58:05AM +0200, Nguyễn Thái Ngọc Duy wrote:\n> \n> > v4 is a cleaned up version of v3. Tests are added. Typos in\n> > git-fetch.txt are corrected. The \"{ -> origin/}master\" format is\n> > dropped.\n> \n> Thanks for continuing to look into this.\n> \n> I tried it on my most-horrible example case, and the results were...just\n> OK. Because the variable-length part of each line comes first, the\n> alignment code means that the \"origin/$\" bit of every line gets bumped\n> out. And if you have a single large branch name, then everybody gets\n> bumped out very far, even to the point of wrapping. E.g., I get\n> something like (with fetch.output=compact, obviously):\n> \n>   From ...\n>    * [new branch]      branch1                      -> origin/$\n>    * [new branch]      branch2                      -> origin/$\n>    * [new branch]      some-really-long-branch-name -> origin/$\n>    + 1234abc..5678def  branch3                      -> origin/$ (forced\n>     update)\n>    * [new branch]      branch4                      -> origin/$\n>\n\nYeah, '$' lowers the chances of wrapping but in corner cases, it'll be\njust as bad.\n\nJunio's suggestion is one way to go. Another is Marc's idea of\nbreaking lines, so we could in theory have something like this\n\n   From ...\n    * [new branch]      branch1    -> origin/$\n    * [new branch]      branch2    -> origin/$\n    * [new branch]      some-really-long-branch-name\n                           -> origin/$\n    + 1234abc..5678def  branch3    -> origin/$ (forced update)\n    * [new branch]      branch4    -> origin/$\n\nOr, for a very long branch name, we could put replace a big chunk of\nit with an ellipsis. If it's long enough, I guess you can still figure\nthe branch name with a big gap in the middle (or left or right).\n\n   From ...\n    * [new branch]      branch1             -> origin/$\n    * [new branch]      branch2             -> origin/$\n    * [new branch]      some-re...anch-name -> origin/$\n    + 1234abc..5678def  branch3             -> origin/$ (forced update)\n    * [new branch]      branch4             -> origin/$\n\nAnd with that we get dangerously close to full customization using\npretty placeholders :)\n\n> One of the nice things about Junio's \"{ -> origin/}\" suggestion is that\n> it puts the variable-length part at the end, so there's no extra\n> alignment required. And you'd get something like:\n> \n>   From ...\n>    * [new branch]      { -> origin/}branch1\n>    * [new branch]      { -> origin/}branch2\n>    * [new branch]      { -> origin/}some-really-long-branch-name\n>    + 1234abc..5678def  { -> origin/}branch3 (forced update)\n>    * [new branch]      { -> origin/}branch4\n> \n> -Peff\n"},{"id":"290600","messageId":"CACsJy8DS3n=7sRGztqe16igrsVaKt3Qa-GRVJBMoZvtEJmdmQA@mail.gmail.com","threadId":"42413","inReplyTo":"20160627184354.GA9594@sigill.intra.peff.net","subject":"Re: [PATCH v4 0/5] Better ref summary alignment in \"git fetch\"","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2016-06-30T16:16:09Z","receivedAt":"2016-06-30T16:18:13Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Mon, Jun 27, 2016 at 8:43 PM, Jeff King <peff@peff.net> wrote:\n> I tried it on my most-horrible example case, and the results were...just\n> OK. Because the variable-length part of each line comes first, the\n> alignment code means that the \"origin/$\" bit of every line gets bumped\n> out. And if you have a single large branch name, then everybody gets\n> bumped out very far, even to the point of wrapping. E.g., I get\n> something like (with fetch.output=compact, obviously):\n>\n>   From ...\n>    * [new branch]      branch1                      -> origin/$\n>    * [new branch]      branch2                      -> origin/$\n>    * [new branch]      some-really-long-branch-name -> origin/$\n>    + 1234abc..5678def  branch3                      -> origin/$ (forced\n>     update)\n>    * [new branch]      branch4                      -> origin/$\n>\n> I've shrunk it a bit to fit in the email; my actual \"long\" name was much\n> larger. And the average length for the shorter ones is, too, but the\n> overall effect is the same; almost every line has a huge run of\n> whitespace. And some lines wrap that would not have even under the\n> normal, duplicated scheme.\n\nI was about to resend with s/\\$/*/g and ignored this issue (with a\nnote) then it occurred to me we can simply ignore these long lines\nfrom column width calculation. Yeah such a line may still be wrapped\naround, but it will not push every other line to the far right. We\nalready have code for that in adjust_refcol_width()\n\n        max    = term_columns();\n        ...\n        /*\n        * rough estimation to see if the output line is too long and\n        * should not be counted (we can't do precise calculation\n        * anyway because we don't know if the error explanation part\n        * will be printed in update_local_ref)\n        */\n        if (21 /* flag and summary */ + rlen + 4 /* -> */ + llen >= max)\n                return;\n        ...\n\nwe can limit max to, like, term_columns() / 2 (or 2/3 of\nterm_columns). There's no perfect number, some people will still find\nthe output ugly _often_. But hopefully the majority won't. What do you\nthink?\n-- \nDuy\n"},{"id":"290631","messageId":"20160701060940.GA5341@sigill.intra.peff.net","threadId":"42413","inReplyTo":"CACsJy8DS3n=7sRGztqe16igrsVaKt3Qa-GRVJBMoZvtEJmdmQA@mail.gmail.com","subject":"Re: [PATCH v4 0/5] Better ref summary alignment in \"git fetch\"","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2016-07-01T06:09:40Z","receivedAt":"2016-07-01T06:09:48Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Jun 30, 2016 at 06:16:09PM +0200, Duy Nguyen wrote:\n\n> > I've shrunk it a bit to fit in the email; my actual \"long\" name was much\n> > larger. And the average length for the shorter ones is, too, but the\n> > overall effect is the same; almost every line has a huge run of\n> > whitespace. And some lines wrap that would not have even under the\n> > normal, duplicated scheme.\n> \n> I was about to resend with s/\\$/*/g and ignored this issue (with a\n> note) then it occurred to me we can simply ignore these long lines\n> from column width calculation. Yeah such a line may still be wrapped\n> around, but it will not push every other line to the far right. We\n> already have code for that in adjust_refcol_width()\n> \n>         max    = term_columns();\n>         ...\n>         /*\n>         * rough estimation to see if the output line is too long and\n>         * should not be counted (we can't do precise calculation\n>         * anyway because we don't know if the error explanation part\n>         * will be printed in update_local_ref)\n>         */\n>         if (21 /* flag and summary */ + rlen + 4 /* -> */ + llen >= max)\n>                 return;\n>         ...\n> \n> we can limit max to, like, term_columns() / 2 (or 2/3 of\n> term_columns). There's no perfect number, some people will still find\n> the output ugly _often_. But hopefully the majority won't. What do you\n> think?\n\nWithout having seen the output, my gut feeling is \"meh\". It will\nhopefully avoid the wrapping, and it will reduce the size of the huge\nwhitespace gap, but not eliminate it. So I think it will still be\nsomewhat ugly.\n\n-Peff\n"},{"id":"290673","messageId":"20160701160331.29252-1-pclouds@gmail.com","threadId":"42413","inReplyTo":"20160626055810.26960-1-pclouds@gmail.com","subject":"[PATCH v5 0/5] Better ref summary alignment in \"git fetch\"","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-07-01T16:03:26Z","receivedAt":"2016-07-01T16:03:53Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"v5 changes the substitute symbol from '$' to '*' in compact mode and\nmakes sure long lines in compact mode will not make the remote ref\ncolumn too big (it's far from perfect but I think it's still good to\ndo).\n\nI'm not sure if we should bring back \"{ -> origin/}foo\" format. I can\ndo it if someone still wants it.\n\nInterdiff\n\ndiff --git a/Documentation/git-fetch.txt b/Documentation/git-fetch.txt\nindex 61e8885..9e42169 100644\n--- a/Documentation/git-fetch.txt\n+++ b/Documentation/git-fetch.txt\n@@ -116,10 +116,10 @@ representing the status of a single ref. Each line is of the form:\n The status of up-to-date refs is shown only if the --verbose option is\n used.\n \n-In compact output mode, if either entire `<from>` or `<to>` is found\n-in the other string, it will be substituted with `$` in the other\n-string. or example, `master -> origin/master` becomes\n-`master -> origin/$`.\n+In compact output mode, specified with configuration variable\n+fetch.output, if either entire `<from>` or `<to>` is found in the\n+other string, it will be substituted with `*` in the other string. For\n+example, `master -> origin/master` becomes `master -> origin/*`.\n \n flag::\n \tA single character indicating the status of the ref:\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 9d9f4e8..0a2eed1 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -455,7 +455,7 @@ static int compact_format;\n \n static void adjust_refcol_width(const struct ref *ref)\n {\n-\tint max, rlen, llen;\n+\tint max, rlen, llen, len;\n \n \t/* uptodate lines are only shown on high verbosity level */\n \tif (!verbosity && !oidcmp(&ref->peer_ref->old_oid, &ref->old_oid))\n@@ -464,16 +464,6 @@ static void adjust_refcol_width(const struct ref *ref)\n \tmax    = term_columns();\n \trlen   = utf8_strwidth(prettify_refname(ref->name));\n \n-\tif (compact_format) {\n-\t\t/*\n-\t\t * Not precise calculation because '$' can appear in\n-\t\t * ref->name and reduce actual length.\n-\t\t */\n-\t\tif (refcol_width < rlen)\n-\t\t\trefcol_width = rlen;\n-\t\treturn;\n-\t}\n-\n \tllen   = utf8_strwidth(prettify_refname(ref->peer_ref->name));\n \n \t/*\n@@ -482,9 +472,19 @@ static void adjust_refcol_width(const struct ref *ref)\n \t * anyway because we don't know if the error explanation part\n \t * will be printed in update_local_ref)\n \t */\n-\tif (21 /* flag and summary */ + rlen + 4 /* -> */ + llen >= max)\n+\tif (compact_format) {\n+\t\tllen = 0;\n+\t\tmax = max * 2 / 3;\n+\t}\n+\tlen = 21 /* flag and summary */ + rlen + 4 /* -> */ + llen;\n+\tif (len >= max)\n \t\treturn;\n \n+\t/*\n+\t * Not precise calculation for compact mode because '*' can\n+\t * appear on the left hand side of '->' and shrink the column\n+\t * back.\n+\t */\n \tif (refcol_width < rlen)\n \t\trefcol_width = rlen;\n }\n@@ -519,7 +519,9 @@ static void print_remote_to_local(struct strbuf *display,\n \tstrbuf_addf(display, \"%-*s -> %s\", refcol_width, remote, local);\n }\n \n-static int dollarize(struct strbuf *haystack, const char *needle)\n+static int find_and_replace(struct strbuf *haystack,\n+\t\t\t    const char *needle,\n+\t\t\t    const char *placeholder)\n {\n \tconst char *p = strstr(haystack->buf, needle);\n \tint plen, nlen;\n@@ -535,7 +537,8 @@ static int dollarize(struct strbuf *haystack, const char *needle)\n \tif (plen > nlen && p[nlen] != '/')\n \t\treturn 0;\n \n-\tstrbuf_splice(haystack, p - haystack->buf, nlen, \"$\", 1);\n+\tstrbuf_splice(haystack, p - haystack->buf, nlen,\n+\t\t      placeholder, strlen(placeholder));\n \treturn 1;\n }\n \n@@ -546,15 +549,15 @@ static void print_compact(struct strbuf *display,\n \tstruct strbuf l = STRBUF_INIT;\n \n \tif (!strcmp(remote, local)) {\n-\t\tstrbuf_addf(display, \"%-*s -> $\", refcol_width, remote);\n+\t\tstrbuf_addf(display, \"%-*s -> *\", refcol_width, remote);\n \t\treturn;\n \t}\n \n \tstrbuf_addstr(&r, remote);\n \tstrbuf_addstr(&l, local);\n \n-\tif (!dollarize(&r, local))\n-\t\tdollarize(&l, remote);\n+\tif (!find_and_replace(&r, local, \"*\"))\n+\t\tfind_and_replace(&l, remote, \"*\");\n \tprint_remote_to_local(display, r.buf, l.buf);\n \n \tstrbuf_release(&r);\ndiff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\nindex 3a92718..6032776 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -712,8 +712,8 @@ test_expect_success 'fetch compact output' '\n \t\t\tgrep -e \"->\" | cut -c 22- >../actual\n \t) &&\n \tcat >expect <<-\\EOF &&\n-\tmaster     -> origin/$\n-\textraaa    -> $\n+\tmaster     -> origin/*\n+\textraaa    -> *\n \tEOF\n \ttest_cmp expect actual\n '\n\nNguyễn Thái Ngọc Duy (5):\n  git-fetch.txt: document fetch output\n  fetch: refactor ref update status formatting code\n  fetch: change flag code for displaying tag update and deleted ref\n  fetch: align all \"remote -> local\" output\n  fetch: reduce duplicate in ref update status lines with placeholder\n\n Documentation/config.txt    |   5 ++\n Documentation/git-fetch.txt |  51 ++++++++++++\n builtin/fetch.c             | 197 +++++++++++++++++++++++++++++++++++---------\n t/t5510-fetch.sh            |  30 +++++++\n 4 files changed, 243 insertions(+), 40 deletions(-)\n\n-- \n2.8.2.531.gd073806\n\n"},{"id":"290674","messageId":"20160701160331.29252-2-pclouds@gmail.com","threadId":"42413","inReplyTo":"20160701160331.29252-1-pclouds@gmail.com","subject":"[PATCH v5 1/5] git-fetch.txt: document fetch output","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-07-01T16:03:27Z","receivedAt":"2016-07-01T16:04:00Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This documents the ref update status of fetch. The structure of this\noutput is defined in [1]. The ouput content is refined a bit in [2]\n[3] [4].\n\nThis patch is a copy from git-push.txt, modified a bit because the\nflag '-' means different things in push (delete) and fetch (tag\nupdate).\n\nPS. For code archaeologists, the discussion mentioned in [1] is\nprobably [5].\n\n[1] 165f390 (git-fetch: more terse fetch output - 2007-11-03)\n[2] 6315472 (fetch: report local storage errors ... - 2008-06-26)\n[3] f360d84 (builtin-fetch: add --prune option - 2009-11-10)\n[4] 0997ada (fetch: describe new refs based on where... - 2012-04-16)\n[5] http://thread.gmane.org/gmane.comp.version-control.git/61657\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-fetch.txt | 46 +++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 46 insertions(+)\n\ndiff --git a/Documentation/git-fetch.txt b/Documentation/git-fetch.txt\nindex efe56e0..cbf441f 100644\n--- a/Documentation/git-fetch.txt\n+++ b/Documentation/git-fetch.txt\n@@ -99,6 +99,52 @@ The latter use of the `remote.<repository>.fetch` values can be\n overridden by giving the `--refmap=<refspec>` parameter(s) on the\n command line.\n \n+OUTPUT\n+------\n+\n+The output of \"git fetch\" depends on the transport method used; this\n+section describes the output when fetching over the Git protocol\n+(either locally or via ssh) and Smart HTTP protocol.\n+\n+The status of the fetch is output in tabular form, with each line\n+representing the status of a single ref. Each line is of the form:\n+\n+-------------------------------\n+ <flag> <summary> <from> -> <to> [<reason>]\n+-------------------------------\n+\n+The status of up-to-date refs is shown only if the --verbose option is\n+used.\n+\n+flag::\n+\tA single character indicating the status of the ref:\n+(space);; for a successfully fetched fast-forward;\n+`+`;; for a successful forced update;\n+`x`;; for a successfully pruned ref;\n+`-`;; for a successful tag update;\n+`*`;; for a successfully fetched new ref;\n+`!`;; for a ref that was rejected or failed to update; and\n+`=`;; for a ref that was up to date and did not need fetching.\n+\n+summary::\n+\tFor a successfully fetched ref, the summary shows the old and new\n+\tvalues of the ref in a form suitable for using as an argument to\n+\t`git log` (this is `<old>..<new>` in most cases, and\n+\t`<old>...<new>` for forced non-fast-forward updates).\n+\n+from::\n+\tThe name of the remote ref being fetched from, minus its\n+\t`refs/<type>/` prefix. In the case of deletion, the name of\n+\tthe remote ref is \"(none)\".\n+\n+to::\n+\tThe name of the local ref being updated, minus its\n+\t`refs/<type>/` prefix.\n+\n+reason::\n+\tA human-readable explanation. In the case of successfully fetched\n+\trefs, no explanation is needed. For a failed ref, the reason for\n+\tfailure is described.\n \n EXAMPLES\n --------\n-- \n2.8.2.531.gd073806\n\n"},{"id":"290675","messageId":"20160701160331.29252-3-pclouds@gmail.com","threadId":"42413","inReplyTo":"20160701160331.29252-1-pclouds@gmail.com","subject":"[PATCH v5 2/5] fetch: refactor ref update status formatting code","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-07-01T16:03:28Z","receivedAt":"2016-07-01T16:04:03Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This makes it easier to change the formatting later. And it makes sure\ntranslators cannot mess up format specifiers and break Git.\n\nThere are a couple call sites where the length of the second column is\nTRANSPORT_SUMMARY_WIDTH instead of calculated by TRANSPORT_SUMMARY(),\nwhich is enforced now. The result should be the same because these call\nsites do not contain characters outside ASCII range.\n\nThe two strbuf_addf() calls instead of one is mostly to reduce\ndiff-noise in a future patch where \"ref -> ref\" is reformatted\ndifferently.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/fetch.c | 77 ++++++++++++++++++++++++++++-----------------------------\n 1 file changed, 38 insertions(+), 39 deletions(-)\n\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 1582ca7..a7f152a 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -451,6 +451,16 @@ fail:\n \n #define REFCOL_WIDTH  10\n \n+static void format_display(struct strbuf *display, char code,\n+\t\t\t   const char *summary, const char *error,\n+\t\t\t   const char *remote, const char *local)\n+{\n+\tstrbuf_addf(display, \"%c %-*s \", code, TRANSPORT_SUMMARY(summary));\n+\tstrbuf_addf(display, \"%-*s -> %s\", REFCOL_WIDTH, remote, local);\n+\tif (error)\n+\t\tstrbuf_addf(display, \"  (%s)\", error);\n+}\n+\n static int update_local_ref(struct ref *ref,\n \t\t\t    const char *remote,\n \t\t\t    const struct ref *remote_ref,\n@@ -467,9 +477,8 @@ static int update_local_ref(struct ref *ref,\n \n \tif (!oidcmp(&ref->old_oid, &ref->new_oid)) {\n \t\tif (verbosity > 0)\n-\t\t\tstrbuf_addf(display, \"= %-*s %-*s -> %s\",\n-\t\t\t\t    TRANSPORT_SUMMARY(_(\"[up to date]\")),\n-\t\t\t\t    REFCOL_WIDTH, remote, pretty_ref);\n+\t\t\tformat_display(display, '=', _(\"[up to date]\"), NULL,\n+\t\t\t\t       remote, pretty_ref);\n \t\treturn 0;\n \t}\n \n@@ -481,10 +490,9 @@ static int update_local_ref(struct ref *ref,\n \t\t * If this is the head, and it's not okay to update\n \t\t * the head, and the old value of the head isn't empty...\n \t\t */\n-\t\tstrbuf_addf(display,\n-\t\t\t    _(\"! %-*s %-*s -> %s  (can't fetch in current branch)\"),\n-\t\t\t    TRANSPORT_SUMMARY(_(\"[rejected]\")),\n-\t\t\t    REFCOL_WIDTH, remote, pretty_ref);\n+\t\tformat_display(display, '!', _(\"[rejected]\"),\n+\t\t\t       _(\"can't fetch in current branch\"),\n+\t\t\t       remote, pretty_ref);\n \t\treturn 1;\n \t}\n \n@@ -492,11 +500,9 @@ static int update_local_ref(struct ref *ref,\n \t    starts_with(ref->name, \"refs/tags/\")) {\n \t\tint r;\n \t\tr = s_update_ref(\"updating tag\", ref, 0);\n-\t\tstrbuf_addf(display, \"%c %-*s %-*s -> %s%s\",\n-\t\t\t    r ? '!' : '-',\n-\t\t\t    TRANSPORT_SUMMARY(_(\"[tag update]\")),\n-\t\t\t    REFCOL_WIDTH, remote, pretty_ref,\n-\t\t\t    r ? _(\"  (unable to update local ref)\") : \"\");\n+\t\tformat_display(display, r ? '!' : '-', _(\"[tag update]\"),\n+\t\t\t       r ? _(\"unable to update local ref\") : NULL,\n+\t\t\t       remote, pretty_ref);\n \t\treturn r;\n \t}\n \n@@ -527,11 +533,9 @@ static int update_local_ref(struct ref *ref,\n \t\t    (recurse_submodules != RECURSE_SUBMODULES_ON))\n \t\t\tcheck_for_new_submodule_commits(ref->new_oid.hash);\n \t\tr = s_update_ref(msg, ref, 0);\n-\t\tstrbuf_addf(display, \"%c %-*s %-*s -> %s%s\",\n-\t\t\t    r ? '!' : '*',\n-\t\t\t    TRANSPORT_SUMMARY(what),\n-\t\t\t    REFCOL_WIDTH, remote, pretty_ref,\n-\t\t\t    r ? _(\"  (unable to update local ref)\") : \"\");\n+\t\tformat_display(display, r ? '!' : '*', what,\n+\t\t\t       r ? _(\"unable to update local ref\") : NULL,\n+\t\t\t       remote, pretty_ref);\n \t\treturn r;\n \t}\n \n@@ -545,11 +549,9 @@ static int update_local_ref(struct ref *ref,\n \t\t    (recurse_submodules != RECURSE_SUBMODULES_ON))\n \t\t\tcheck_for_new_submodule_commits(ref->new_oid.hash);\n \t\tr = s_update_ref(\"fast-forward\", ref, 1);\n-\t\tstrbuf_addf(display, \"%c %-*s %-*s -> %s%s\",\n-\t\t\t    r ? '!' : ' ',\n-\t\t\t    TRANSPORT_SUMMARY_WIDTH, quickref.buf,\n-\t\t\t    REFCOL_WIDTH, remote, pretty_ref,\n-\t\t\t    r ? _(\"  (unable to update local ref)\") : \"\");\n+\t\tformat_display(display, r ? '!' : ' ', quickref.buf,\n+\t\t\t       r ? _(\"unable to update local ref\") : NULL,\n+\t\t\t       remote, pretty_ref);\n \t\tstrbuf_release(&quickref);\n \t\treturn r;\n \t} else if (force || ref->force) {\n@@ -562,18 +564,14 @@ static int update_local_ref(struct ref *ref,\n \t\t    (recurse_submodules != RECURSE_SUBMODULES_ON))\n \t\t\tcheck_for_new_submodule_commits(ref->new_oid.hash);\n \t\tr = s_update_ref(\"forced-update\", ref, 1);\n-\t\tstrbuf_addf(display, \"%c %-*s %-*s -> %s  (%s)\",\n-\t\t\t    r ? '!' : '+',\n-\t\t\t    TRANSPORT_SUMMARY_WIDTH, quickref.buf,\n-\t\t\t    REFCOL_WIDTH, remote, pretty_ref,\n-\t\t\t    r ? _(\"unable to update local ref\") : _(\"forced update\"));\n+\t\tformat_display(display, r ? '!' : '+', quickref.buf,\n+\t\t\t       r ? _(\"unable to update local ref\") : _(\"forced update\"),\n+\t\t\t       remote, pretty_ref);\n \t\tstrbuf_release(&quickref);\n \t\treturn r;\n \t} else {\n-\t\tstrbuf_addf(display, \"! %-*s %-*s -> %s  %s\",\n-\t\t\t    TRANSPORT_SUMMARY(_(\"[rejected]\")),\n-\t\t\t    REFCOL_WIDTH, remote, pretty_ref,\n-\t\t\t    _(\"(non-fast-forward)\"));\n+\t\tformat_display(display, '!', _(\"[rejected]\"), _(\"non-fast-forward\"),\n+\t\t\t       remote, pretty_ref);\n \t\treturn 1;\n \t}\n }\n@@ -714,11 +712,10 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,\n \t\t\t\trc |= update_local_ref(ref, what, rm, &note);\n \t\t\t\tfree(ref);\n \t\t\t} else\n-\t\t\t\tstrbuf_addf(&note, \"* %-*s %-*s -> FETCH_HEAD\",\n-\t\t\t\t\t    TRANSPORT_SUMMARY_WIDTH,\n-\t\t\t\t\t    *kind ? kind : \"branch\",\n-\t\t\t\t\t    REFCOL_WIDTH,\n-\t\t\t\t\t    *what ? what : \"HEAD\");\n+\t\t\t\tformat_display(&note, '*',\n+\t\t\t\t\t       *kind ? kind : \"branch\", NULL,\n+\t\t\t\t\t       *what ? what : \"HEAD\",\n+\t\t\t\t\t       \"FETCH_HEAD\");\n \t\t\tif (note.len) {\n \t\t\t\tif (verbosity >= 0 && !shown_url) {\n \t\t\t\t\tfprintf(stderr, _(\"From %.*s\\n\"),\n@@ -812,13 +809,15 @@ static int prune_refs(struct refspec *refs, int ref_count, struct ref *ref_map,\n \n \tif (verbosity >= 0) {\n \t\tfor (ref = stale_refs; ref; ref = ref->next) {\n+\t\t\tstruct strbuf sb = STRBUF_INIT;\n \t\t\tif (!shown_url) {\n \t\t\t\tfprintf(stderr, _(\"From %.*s\\n\"), url_len, url);\n \t\t\t\tshown_url = 1;\n \t\t\t}\n-\t\t\tfprintf(stderr, \" x %-*s %-*s -> %s\\n\",\n-\t\t\t\tTRANSPORT_SUMMARY(_(\"[deleted]\")),\n-\t\t\t\tREFCOL_WIDTH, _(\"(none)\"), prettify_refname(ref->name));\n+\t\t\tformat_display(&sb, 'x', _(\"[deleted]\"), NULL,\n+\t\t\t\t       _(\"(none)\"), prettify_refname(ref->name));\n+\t\t\tfprintf(stderr, \" %s\\n\",sb.buf);\n+\t\t\tstrbuf_release(&sb);\n \t\t\twarn_dangling_symref(stderr, dangling_msg, ref->name);\n \t\t}\n \t}\n-- \n2.8.2.531.gd073806\n\n"},{"id":"290676","messageId":"20160701160331.29252-6-pclouds@gmail.com","threadId":"42413","inReplyTo":"20160701160331.29252-1-pclouds@gmail.com","subject":"[PATCH v5 5/5] fetch: reduce duplicate in ref update status lines with placeholder","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-07-01T16:03:31Z","receivedAt":"2016-07-01T16:04:08Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"In the \"remote -> local\" line, if either ref is a substring of the\nother, the common part in the other string is replaced with \"*\". For\nexample\n\n    abc                -> origin/abc\n    refs/pull/123/head -> pull/123\n\nbecome\n\n    abc         -> origin/*\n    refs/*/head -> pull/123\n\nActivated with fetch.output=compact.\n\nFor the record, this output is not perfect. A single giant ref can\npush all refs very far to the right and likely be wrapped around. We\nmay have a few options:\n\n - exclude these long lines smarter\n\n - break the line after \"->\", exclude it from column width calculation\n\n - implement a new format, { -> origin/}foo, which makes the problem\n   go away at the cost of a bit harder to read\n\n - reverse all the arrows so we have \"* <- looong-ref\", again still\n   hard to read.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/config.txt    |  5 +++\n Documentation/git-fetch.txt |  5 +++\n builtin/fetch.c             | 77 ++++++++++++++++++++++++++++++++++++++++++++-\n t/t5510-fetch.sh            | 17 +++++++++-\n 4 files changed, 102 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 2e1b2e4..7f6e58d 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -1220,6 +1220,11 @@ fetch.prune::\n \tIf true, fetch will automatically behave as if the `--prune`\n \toption was given on the command line.  See also `remote.<name>.prune`.\n \n+fetch.output::\n+\tControl how ref update status is printed. Valid values are\n+\t`full` and `compact`. Default value is `full`. See section\n+\tOUTPUT in linkgit:git-fetch[1] for detail.\n+\n format.attach::\n \tEnable multipart/mixed attachments as the default for\n \t'format-patch'.  The value can also be a double quoted string\ndiff --git a/Documentation/git-fetch.txt b/Documentation/git-fetch.txt\nindex 771dde5..9e42169 100644\n--- a/Documentation/git-fetch.txt\n+++ b/Documentation/git-fetch.txt\n@@ -116,6 +116,11 @@ representing the status of a single ref. Each line is of the form:\n The status of up-to-date refs is shown only if the --verbose option is\n used.\n \n+In compact output mode, specified with configuration variable\n+fetch.output, if either entire `<from>` or `<to>` is found in the\n+other string, it will be substituted with `*` in the other string. For\n+example, `master -> origin/master` becomes `master -> origin/*`.\n+\n flag::\n \tA single character indicating the status of the ref:\n (space);; for a successfully fetched fast-forward;\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 2bc609b..0a2eed1 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -451,6 +451,7 @@ fail:\n }\n \n static int refcol_width = 10;\n+static int compact_format;\n \n static void adjust_refcol_width(const struct ref *ref)\n {\n@@ -462,6 +463,7 @@ static void adjust_refcol_width(const struct ref *ref)\n \n \tmax    = term_columns();\n \trlen   = utf8_strwidth(prettify_refname(ref->name));\n+\n \tllen   = utf8_strwidth(prettify_refname(ref->peer_ref->name));\n \n \t/*\n@@ -470,10 +472,19 @@ static void adjust_refcol_width(const struct ref *ref)\n \t * anyway because we don't know if the error explanation part\n \t * will be printed in update_local_ref)\n \t */\n+\tif (compact_format) {\n+\t\tllen = 0;\n+\t\tmax = max * 2 / 3;\n+\t}\n \tlen = 21 /* flag and summary */ + rlen + 4 /* -> */ + llen;\n \tif (len >= max)\n \t\treturn;\n \n+\t/*\n+\t * Not precise calculation for compact mode because '*' can\n+\t * appear on the left hand side of '->' and shrink the column\n+\t * back.\n+\t */\n \tif (refcol_width < rlen)\n \t\trefcol_width = rlen;\n }\n@@ -481,6 +492,16 @@ static void adjust_refcol_width(const struct ref *ref)\n static void prepare_format_display(struct ref *ref_map)\n {\n \tstruct ref *rm;\n+\tconst char *format = \"full\";\n+\n+\tgit_config_get_string_const(\"fetch.output\", &format);\n+\tif (!strcasecmp(format, \"full\"))\n+\t\tcompact_format = 0;\n+\telse if (!strcasecmp(format, \"compact\"))\n+\t\tcompact_format = 1;\n+\telse\n+\t\tdie(_(\"configuration fetch.output contains invalid value %s\"),\n+\t\t    format);\n \n \tfor (rm = ref_map; rm; rm = rm->next) {\n \t\tif (rm->status == REF_STATUS_REJECT_SHALLOW ||\n@@ -492,12 +513,66 @@ static void prepare_format_display(struct ref *ref_map)\n \t}\n }\n \n+static void print_remote_to_local(struct strbuf *display,\n+\t\t\t\t  const char *remote, const char *local)\n+{\n+\tstrbuf_addf(display, \"%-*s -> %s\", refcol_width, remote, local);\n+}\n+\n+static int find_and_replace(struct strbuf *haystack,\n+\t\t\t    const char *needle,\n+\t\t\t    const char *placeholder)\n+{\n+\tconst char *p = strstr(haystack->buf, needle);\n+\tint plen, nlen;\n+\n+\tif (!p)\n+\t\treturn 0;\n+\n+\tif (p > haystack->buf && p[-1] != '/')\n+\t\treturn 0;\n+\n+\tplen = strlen(p);\n+\tnlen = strlen(needle);\n+\tif (plen > nlen && p[nlen] != '/')\n+\t\treturn 0;\n+\n+\tstrbuf_splice(haystack, p - haystack->buf, nlen,\n+\t\t      placeholder, strlen(placeholder));\n+\treturn 1;\n+}\n+\n+static void print_compact(struct strbuf *display,\n+\t\t\t  const char *remote, const char *local)\n+{\n+\tstruct strbuf r = STRBUF_INIT;\n+\tstruct strbuf l = STRBUF_INIT;\n+\n+\tif (!strcmp(remote, local)) {\n+\t\tstrbuf_addf(display, \"%-*s -> *\", refcol_width, remote);\n+\t\treturn;\n+\t}\n+\n+\tstrbuf_addstr(&r, remote);\n+\tstrbuf_addstr(&l, local);\n+\n+\tif (!find_and_replace(&r, local, \"*\"))\n+\t\tfind_and_replace(&l, remote, \"*\");\n+\tprint_remote_to_local(display, r.buf, l.buf);\n+\n+\tstrbuf_release(&r);\n+\tstrbuf_release(&l);\n+}\n+\n static void format_display(struct strbuf *display, char code,\n \t\t\t   const char *summary, const char *error,\n \t\t\t   const char *remote, const char *local)\n {\n \tstrbuf_addf(display, \"%c %-*s \", code, TRANSPORT_SUMMARY(summary));\n-\tstrbuf_addf(display, \"%-*s -> %s\", refcol_width, remote, local);\n+\tif (!compact_format)\n+\t\tprint_remote_to_local(display, remote, local);\n+\telse\n+\t\tprint_compact(display, remote, local);\n \tif (error)\n \t\tstrbuf_addf(display, \"  (%s)\", error);\n }\ndiff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\nindex f50497e..6032776 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -693,7 +693,7 @@ test_expect_success 'fetch aligned output' '\n \ttest_commit looooooooooooong-tag &&\n \t(\n \t\tcd full-output &&\n-\t\tgit fetch origin 2>&1 | \\\n+\t\tgit -c fetch.output=full fetch origin 2>&1 | \\\n \t\t\tgrep -e \"->\" | cut -c 22- >../actual\n \t) &&\n \tcat >expect <<-\\EOF &&\n@@ -703,4 +703,19 @@ test_expect_success 'fetch aligned output' '\n \ttest_cmp expect actual\n '\n \n+test_expect_success 'fetch compact output' '\n+\tgit clone . compact &&\n+\ttest_commit extraaa &&\n+\t(\n+\t\tcd compact &&\n+\t\tgit -c fetch.output=compact fetch origin 2>&1 | \\\n+\t\t\tgrep -e \"->\" | cut -c 22- >../actual\n+\t) &&\n+\tcat >expect <<-\\EOF &&\n+\tmaster     -> origin/*\n+\textraaa    -> *\n+\tEOF\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n2.8.2.531.gd073806\n\n"},{"id":"290677","messageId":"20160701160331.29252-4-pclouds@gmail.com","threadId":"42413","inReplyTo":"20160701160331.29252-1-pclouds@gmail.com","subject":"[PATCH v5 3/5] fetch: change flag code for displaying tag update and deleted ref","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-07-01T16:03:29Z","receivedAt":"2016-07-01T16:04:09Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This makes the fetch flag code consistent with push, where '-' means\ndeleted ref.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-fetch.txt | 4 ++--\n builtin/fetch.c             | 4 ++--\n 2 files changed, 4 insertions(+), 4 deletions(-)\n\ndiff --git a/Documentation/git-fetch.txt b/Documentation/git-fetch.txt\nindex cbf441f..771dde5 100644\n--- a/Documentation/git-fetch.txt\n+++ b/Documentation/git-fetch.txt\n@@ -120,8 +120,8 @@ flag::\n \tA single character indicating the status of the ref:\n (space);; for a successfully fetched fast-forward;\n `+`;; for a successful forced update;\n-`x`;; for a successfully pruned ref;\n-`-`;; for a successful tag update;\n+`-`;; for a successfully pruned ref;\n+`t`;; for a successful tag update;\n `*`;; for a successfully fetched new ref;\n `!`;; for a ref that was rejected or failed to update; and\n `=`;; for a ref that was up to date and did not need fetching.\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex a7f152a..8177f90 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -500,7 +500,7 @@ static int update_local_ref(struct ref *ref,\n \t    starts_with(ref->name, \"refs/tags/\")) {\n \t\tint r;\n \t\tr = s_update_ref(\"updating tag\", ref, 0);\n-\t\tformat_display(display, r ? '!' : '-', _(\"[tag update]\"),\n+\t\tformat_display(display, r ? '!' : 't', _(\"[tag update]\"),\n \t\t\t       r ? _(\"unable to update local ref\") : NULL,\n \t\t\t       remote, pretty_ref);\n \t\treturn r;\n@@ -814,7 +814,7 @@ static int prune_refs(struct refspec *refs, int ref_count, struct ref *ref_map,\n \t\t\t\tfprintf(stderr, _(\"From %.*s\\n\"), url_len, url);\n \t\t\t\tshown_url = 1;\n \t\t\t}\n-\t\t\tformat_display(&sb, 'x', _(\"[deleted]\"), NULL,\n+\t\t\tformat_display(&sb, '-', _(\"[deleted]\"), NULL,\n \t\t\t\t       _(\"(none)\"), prettify_refname(ref->name));\n \t\t\tfprintf(stderr, \" %s\\n\",sb.buf);\n \t\t\tstrbuf_release(&sb);\n-- \n2.8.2.531.gd073806\n\n"},{"id":"290680","messageId":"20160701160331.29252-5-pclouds@gmail.com","threadId":"42413","inReplyTo":"20160701160331.29252-1-pclouds@gmail.com","subject":"[PATCH v5 4/5] fetch: align all \"remote -> local\" output","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2016-07-01T16:03:30Z","receivedAt":"2016-07-01T16:20:19Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"We do align \"remote -> local\" output by allocating 10 columns to\n\"remote\". That produces aligned output only for short refs. An extra\npass is performed to find the longest remote ref name (that does not\nproduce a line longer than terminal width) to produce better aligned\noutput.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n builtin/fetch.c  | 47 +++++++++++++++++++++++++++++++++++++++++++++--\n t/t5510-fetch.sh | 15 +++++++++++++++\n 2 files changed, 60 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 8177f90..2bc609b 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -15,6 +15,7 @@\n #include \"submodule.h\"\n #include \"connected.h\"\n #include \"argv-array.h\"\n+#include \"utf8.h\"\n \n static const char * const builtin_fetch_usage[] = {\n \tN_(\"git fetch [<options>] [<repository> [<refspec>...]]\"),\n@@ -449,14 +450,54 @@ fail:\n \t\t\t   : STORE_REF_ERROR_OTHER;\n }\n \n-#define REFCOL_WIDTH  10\n+static int refcol_width = 10;\n+\n+static void adjust_refcol_width(const struct ref *ref)\n+{\n+\tint max, rlen, llen, len;\n+\n+\t/* uptodate lines are only shown on high verbosity level */\n+\tif (!verbosity && !oidcmp(&ref->peer_ref->old_oid, &ref->old_oid))\n+\t\treturn;\n+\n+\tmax    = term_columns();\n+\trlen   = utf8_strwidth(prettify_refname(ref->name));\n+\tllen   = utf8_strwidth(prettify_refname(ref->peer_ref->name));\n+\n+\t/*\n+\t * rough estimation to see if the output line is too long and\n+\t * should not be counted (we can't do precise calculation\n+\t * anyway because we don't know if the error explanation part\n+\t * will be printed in update_local_ref)\n+\t */\n+\tlen = 21 /* flag and summary */ + rlen + 4 /* -> */ + llen;\n+\tif (len >= max)\n+\t\treturn;\n+\n+\tif (refcol_width < rlen)\n+\t\trefcol_width = rlen;\n+}\n+\n+static void prepare_format_display(struct ref *ref_map)\n+{\n+\tstruct ref *rm;\n+\n+\tfor (rm = ref_map; rm; rm = rm->next) {\n+\t\tif (rm->status == REF_STATUS_REJECT_SHALLOW ||\n+\t\t    !rm->peer_ref ||\n+\t\t    !strcmp(rm->name, \"HEAD\"))\n+\t\t\tcontinue;\n+\n+\t\tadjust_refcol_width(rm);\n+\t}\n+}\n \n static void format_display(struct strbuf *display, char code,\n \t\t\t   const char *summary, const char *error,\n \t\t\t   const char *remote, const char *local)\n {\n \tstrbuf_addf(display, \"%c %-*s \", code, TRANSPORT_SUMMARY(summary));\n-\tstrbuf_addf(display, \"%-*s -> %s\", REFCOL_WIDTH, remote, local);\n+\tstrbuf_addf(display, \"%-*s -> %s\", refcol_width, remote, local);\n \tif (error)\n \t\tstrbuf_addf(display, \"  (%s)\", error);\n }\n@@ -618,6 +659,8 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,\n \t\tgoto abort;\n \t}\n \n+\tprepare_format_display(ref_map);\n+\n \t/*\n \t * We do a pass for each fetch_head_status type in their enum order, so\n \t * merged entries are written before not-for-merge. That lets readers\ndiff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh\nindex 454d896..f50497e 100755\n--- a/t/t5510-fetch.sh\n+++ b/t/t5510-fetch.sh\n@@ -688,4 +688,19 @@ test_expect_success 'fetching with auto-gc does not lock up' '\n \t)\n '\n \n+test_expect_success 'fetch aligned output' '\n+\tgit clone . full-output &&\n+\ttest_commit looooooooooooong-tag &&\n+\t(\n+\t\tcd full-output &&\n+\t\tgit fetch origin 2>&1 | \\\n+\t\t\tgrep -e \"->\" | cut -c 22- >../actual\n+\t) &&\n+\tcat >expect <<-\\EOF &&\n+\tmaster               -> origin/master\n+\tlooooooooooooong-tag -> looooooooooooong-tag\n+\tEOF\n+\ttest_cmp expect actual\n+'\n+\n test_done\n-- \n2.8.2.531.gd073806\n\n"},{"id":"290722","messageId":"xmqqvb0puepd.fsf@gitster.mtv.corp.google.com","threadId":"42413","inReplyTo":"20160701160331.29252-1-pclouds@gmail.com","subject":"Re: [PATCH v5 0/5] Better ref summary alignment in \"git fetch\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-07-01T23:21:50Z","receivedAt":"2016-07-01T23:22:39Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> I'm not sure if we should bring back \"{ -> origin/}foo\" format. I can\n> do it if someone still wants it.\n> ...\n> +In compact output mode, specified with configuration variable\n> +fetch.output, if either entire `<from>` or `<to>` is found in the\n> +other string, it will be substituted with `*` in the other string. For\n> +example, `master -> origin/master` becomes `master -> origin/*`.\n\nWhat is the desired property we would want to see in the end result\nof this series (or possible replacement of it)?  Easier to read by\nhumans?  Cut-and-paste friendliness?  Alignment?\n\nI think the largest objection against \"{ -> origin/}master\" was that\nit wasn't cut-and-paste ready.  There might have been other attempts\nduring the rerolls leading to this v5, but I have to say that what\nwe ended up with, \"master -> origin/*\", is no more cut-and-paste\nfriendly than \"{ -> origin/}master\".\n\nI personally do not care much about cut-and-paste friendliness, and\nI see the cover-letter is titled with \"alignment\", but if the\nalignment is the sole issue, I would have to say \"master -> origin/*\"\nis such a great improvement over \"{ -> origin/}master\".\n\nSo, I do not see strong reason to reject this, but I am not enthused\nby the topic, either.\n"},{"id":"290730","messageId":"CACsJy8CLJ9aSwA_iABdDVrGULyXSgGO1A_Dy=+aOhAn1m26yDQ@mail.gmail.com","threadId":"42413","inReplyTo":"xmqqvb0puepd.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v5 0/5] Better ref summary alignment in \"git fetch\"","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2016-07-02T04:39:34Z","receivedAt":"2016-07-02T04:40:16Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Sat, Jul 2, 2016 at 1:21 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n>\n>> I'm not sure if we should bring back \"{ -> origin/}foo\" format. I can\n>> do it if someone still wants it.\n>> ...\n>> +In compact output mode, specified with configuration variable\n>> +fetch.output, if either entire `<from>` or `<to>` is found in the\n>> +other string, it will be substituted with `*` in the other string. For\n>> +example, `master -> origin/master` becomes `master -> origin/*`.\n>\n> What is the desired property we would want to see in the end result\n> of this series (or possible replacement of it)?  Easier to read by\n> humans?  Cut-and-paste friendliness?  Alignment?\n\nEasier to read, which is largely improved by aligning output.\n\n> I think the largest objection against \"{ -> origin/}master\" was that\n> it wasn't cut-and-paste ready.  There might have been other attempts\n> during the rerolls leading to this v5, but I have to say that what\n> we ended up with, \"master -> origin/*\", is no more cut-and-paste\n> friendly than \"{ -> origin/}master\".\n\nThe true cut-paste friendly is the old output, though \"master ->\norigin/*\" is a tiny bit friendlier than \"{ -> origin/}master\" because\nyou can double click on master in the former to select it (origin/\nrequires some typing, not much with the help of tab-completion).\nDouble clicking on \"origin/}master\" may or may not select master\nalone, depending on your terminal settings.\n-- \nDuy\n"},{"id":"290786","messageId":"577A61F7.6010109@xiplink.com","threadId":"42413","inReplyTo":"20160701160331.29252-1-pclouds@gmail.com","subject":"Re: [PATCH v5 0/5] Better ref summary alignment in \"git fetch\"","fromName":"Marc Branchaud","fromEmail":"marcnarc@xiplink.com","sentAt":"2016-07-04T13:17:43Z","receivedAt":"2016-07-04T13:18:09Z","isPatch":true,"sender":{"key":"marcnarc@xiplink.com","avatar":"https://avatars.githubusercontent.com/u/14980203?v=4"},"body":"On 2016-07-01 12:03 PM, Nguyễn Thái Ngọc Duy wrote:\n> v5 changes the substitute symbol from '$' to '*' in compact mode and\n> makes sure long lines in compact mode will not make the remote ref\n> column too big (it's far from perfect but I think it's still good to\n> do).\n\nI think the first 4 patches are great.\n\nI have no opinion on the 5th patch, as I don't expect to use the compact \nformat in any of its proposed forms (and I can't come up with an \nalternative).\n\n\t\tM.\n\n"},{"id":"290788","messageId":"577A6DBB.2030902@gmail.com","threadId":"42413","inReplyTo":"20160626055810.26960-2-pclouds@gmail.com","subject":"Re: [PATCH v4 1/5] git-fetch.txt: document fetch output","fromName":"Jakub Narębski","fromEmail":"jnareb@gmail.com","sentAt":"2016-07-04T14:07:55Z","receivedAt":"2016-07-04T14:08:07Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"W dniu 2016-06-26 o 07:58, Nguyễn Thái Ngọc Duy pisze:\n> +summary::\n> +\tFor a successfully fetched ref, the summary shows the old and new\n> +\tvalues of the ref in a form suitable for using as an argument to\n> +\t`git log` (this is `<old>..<new>` in most cases, and\n> +\t`<old>...<new>` for forced non-fast-forward updates).\n\nIt would be nice to have documented here also other <summary> formats,\nlike \"[new branch]\", and/or mention that if the <summary> is not usable\nfor `git log` it is put in brackets [].\n\n-- \nJakub Narębski \n\n"},{"id":"290790","messageId":"CACsJy8AGDTgE-Kgj+wNZuiqg9yo_NhyQXEFFJZvAdSts+7R1tA@mail.gmail.com","threadId":"42413","inReplyTo":"577A61F7.6010109@xiplink.com","subject":"Re: [PATCH v5 0/5] Better ref summary alignment in \"git fetch\"","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2016-07-04T15:08:22Z","receivedAt":"2016-07-04T15:08:57Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Mon, Jul 4, 2016 at 3:17 PM, Marc Branchaud <marcnarc@xiplink.com> wrote:\n> On 2016-07-01 12:03 PM, Nguyễn Thái Ngọc Duy wrote:\n>>\n>> v5 changes the substitute symbol from '$' to '*' in compact mode and\n>> makes sure long lines in compact mode will not make the remote ref\n>> column too big (it's far from perfect but I think it's still good to\n>> do).\n>\n>\n> I think the first 4 patches are great.\n>\n> I have no opinion on the 5th patch, as I don't expect to use the compact\n> format in any of its proposed forms (and I can't come up with an\n> alternative).\n\nI started using compact mode for a couple days now and I find it nice.\nBecause line lengths are shortened nearly by half, I can now have two\nvertical tmux panes in a window without wrapping.\n-- \nDuy\n"},{"id":"290791","messageId":"CACsJy8CoSwM9W-HX2X9rA9NbJBVQ8BzySJjp3XjXBK+5r1tXZg@mail.gmail.com","threadId":"42413","inReplyTo":"577A6DBB.2030902@gmail.com","subject":"Re: [PATCH v4 1/5] git-fetch.txt: document fetch output","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2016-07-04T15:17:29Z","receivedAt":"2016-07-04T15:18:04Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Mon, Jul 4, 2016 at 4:07 PM, Jakub Narębski <jnareb@gmail.com> wrote:\n> W dniu 2016-06-26 o 07:58, Nguyễn Thái Ngọc Duy pisze:\n>> +summary::\n>> +     For a successfully fetched ref, the summary shows the old and new\n>> +     values of the ref in a form suitable for using as an argument to\n>> +     `git log` (this is `<old>..<new>` in most cases, and\n>> +     `<old>...<new>` for forced non-fast-forward updates).\n>\n> It would be nice to have documented here also other <summary> formats,\n> like \"[new branch]\", and/or mention that if the <summary> is not usable\n> for `git log` it is put in brackets [].\n\nCan I do it in a separate topic in future? We may want to unify this\nand the output format in git-pull as well, then include fetch format\nin git-pull.\n-- \nDuy\n"},{"id":"290792","messageId":"577A7FFB.6050507@gmail.com","threadId":"42413","inReplyTo":"CACsJy8CoSwM9W-HX2X9rA9NbJBVQ8BzySJjp3XjXBK+5r1tXZg@mail.gmail.com","subject":"Re: [PATCH v4 1/5] git-fetch.txt: document fetch output","fromName":"Jakub Narębski","fromEmail":"jnareb@gmail.com","sentAt":"2016-07-04T15:25:47Z","receivedAt":"2016-07-04T15:26:14Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"W dniu 2016-07-04 o 17:17, Duy Nguyen pisze:\n> On Mon, Jul 4, 2016 at 4:07 PM, Jakub Narębski <jnareb@gmail.com> wrote:\n>> W dniu 2016-06-26 o 07:58, Nguyễn Thái Ngọc Duy pisze:\n>>> +summary::\n>>> +     For a successfully fetched ref, the summary shows the old and new\n>>> +     values of the ref in a form suitable for using as an argument to\n>>> +     `git log` (this is `<old>..<new>` in most cases, and\n>>> +     `<old>...<new>` for forced non-fast-forward updates).\n>>\n>> It would be nice to have documented here also other <summary> formats,\n>> like \"[new branch]\", and/or mention that if the <summary> is not usable\n>> for `git log` it is put in brackets [].\n> \n> Can I do it in a separate topic in future? We may want to unify this\n> and the output format in git-pull as well, then include fetch format\n> in git-pull.\n\nYes, of course it can be added later.  Though it feels strange for docs\nto talk about <old>..<new> and <old>...<new> format only (note that it\nis also suitable for copy'n'paste to `git diff`, not only for `git log`)\nwhile having \"[new branch]\" in examples / later patches.\n\n-- \nJakub Narębski\n\n"},{"id":"290795","messageId":"CACsJy8Crt8d5QdwPP+-ATaseR=gGLb7ri0jOozR==_U0Z1u4YA@mail.gmail.com","threadId":"42413","inReplyTo":"577A7FFB.6050507@gmail.com","subject":"Re: [PATCH v4 1/5] git-fetch.txt: document fetch output","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2016-07-04T15:52:32Z","receivedAt":"2016-07-04T15:53:26Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Mon, Jul 4, 2016 at 5:25 PM, Jakub Narębski <jnareb@gmail.com> wrote:\n> W dniu 2016-07-04 o 17:17, Duy Nguyen pisze:\n>> On Mon, Jul 4, 2016 at 4:07 PM, Jakub Narębski <jnareb@gmail.com> wrote:\n>>> W dniu 2016-06-26 o 07:58, Nguyễn Thái Ngọc Duy pisze:\n>>>> +summary::\n>>>> +     For a successfully fetched ref, the summary shows the old and new\n>>>> +     values of the ref in a form suitable for using as an argument to\n>>>> +     `git log` (this is `<old>..<new>` in most cases, and\n>>>> +     `<old>...<new>` for forced non-fast-forward updates).\n>>>\n>>> It would be nice to have documented here also other <summary> formats,\n>>> like \"[new branch]\", and/or mention that if the <summary> is not usable\n>>> for `git log` it is put in brackets [].\n>>\n>> Can I do it in a separate topic in future? We may want to unify this\n>> and the output format in git-pull as well, then include fetch format\n>> in git-pull.\n>\n> Yes, of course it can be added later.  Though it feels strange for docs\n> to talk about <old>..<new> and <old>...<new> format only (note that it\n> is also suitable for copy'n'paste to `git diff`, not only for `git log`)\n> while having \"[new branch]\" in examples / later patches.\n\nIt's because it's copied from git-push.txt and not all\n[<explaination>] are listed there either :) But I think you have\nconvinced me I should do it now.\n-- \nDuy\n"}]}