{"thread":{"id":"8312","subject":"[PATCH] Fix mishandling of $Id$ expanded in the repository copy in convert.c","startedAt":"2007-05-25T10:50:08Z","lastAt":"2007-05-27T10:52:11Z","messageCount":13,"participants":["Andy Parkins","Joshua N Pritikin","Nicolas Pitre","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"43230","messageId":"200705251150.09439.andyparkins@gmail.com","threadId":"8312","inReplyTo":null,"subject":"[PATCH] Fix mishandling of $Id$ expanded in the repository copy in convert.c","fromName":"Andy Parkins","fromEmail":"andyparkins@gmail.com","sentAt":"2007-05-25T10:50:08Z","receivedAt":"2007-05-25T10:50:08Z","isPatch":true,"sender":{"key":"andyparkins@gmail.com","avatar":null},"body":"If the repository contained an expanded ident keyword (i.e. $Id:XXXX$),\nthen the wrong bytes were discarded, and the Id keyword was not\nexpanded.  The fault was in convert.c:ident_to_worktree().\n\nPreviously, when a \"$Id:\" was found in the repository version,\nident_to_worktree() would search for the next \"$\" after this, and\ndiscarded everything it found until then.  That was done with the loop:\n\n    do {\n        ch = *cp++;\n        if (ch == '$')\n            break;\n        rem--;\n    } while (rem);\n\nThe above loop left cp pointing one character _after_ the final \"$\"\n(because of ch = *cp++).  This was different from the non-expanded case,\nwere cp is left pointing at the \"$\", and was different from the comment\nwhich stated \"discard up to but not including the closing $\".  This\npatch fixes that by making the loop:\n\n    do {\n        ch = *cp;\n        if (ch == '$')\n            break;\n        cp++;\n        rem--;\n    } while (rem);\n\nThat is, cp is tested _then_ incremented.\n\nThis loop exits if it finds a \"$\" or if it runs out of bytes in the\nsource.  After this loop, if there was no closing \"$\" the expansion is\nskipped, and the outer loop is allowed to continue leaving this\nnon-keyword as it was.  However, when the \"$\" is found, size is\ncorrected, before running the expansion:\n\n    size -= (cp - src);\n\nThis is wrong; size is going to be corrected anyway after the expansion,\nso there is no need to do it here.  This patch removes that redundant\ncorrection.\n\nTo help find this bug, I heavily commented the routine; those comments\nare included here as a bonus.\n\nSigned-off-by: Andy Parkins <andyparkins@gmail.com>\n---\nYou wouldn't believe that I managed to get a file into the repository\nwith $Id$ stored expanded would you?  :-)\n\nAnyway, it's fortunate that I did, because it revealed the above bugs\nin the ident_to_worktree() code.\n\nI've included the comments I wrote while debugging in this patch, which\nI'm sure will annoy you, because you'd rather the fix and the comments\nseparately.  I'll supply that if you wish - just holler.\n\n convert.c |   39 +++++++++++++++++++++++++++++++++++++--\n 1 files changed, 37 insertions(+), 2 deletions(-)\n\ndiff --git a/convert.c b/convert.c\nindex 4b26b1a..3c44e3d 100644\n--- a/convert.c\n+++ b/convert.c\n@@ -509,36 +509,71 @@ static char *ident_to_worktree(const char *path, const char *src, unsigned long\n \n \tfor (dst = buf; size; size--) {\n \t\tconst char *cp;\n+\t\t/* Fetch next source character, move the pointer on */\n \t\tchar ch = *src++;\n+\t\t/* Copy the current character to the destination */\n \t\t*dst++ = ch;\n+\t\t/* If the current character is \"$\" or there are less than three\n+\t\t * remaining bytes or the two bytes following this one are not\n+\t\t * \"Id\", then simply read the next character */\n \t\tif ((ch != '$') || (size < 3) || memcmp(\"Id\", src, 2))\n \t\t\tcontinue;\n+\t\t/*\n+\t\t * Here when\n+\t\t *  - There are more than 2 bytes remaining\n+\t\t *  - The current three bytes are \"$Id$\"\n+\t\t * with\n+\t\t *  - ch == \"$\"\n+\t\t *  - src[0] == \"I\"\n+\t\t */\n \n+\t\t/*\n+\t\t * It's possible that an expanded Id has crept its way into the\n+\t\t * repository, we cope with that by stripping the expansion out\n+\t\t */\n \t\tif (src[2] == ':') {\n+\t\t\t/* Expanded keywords have \"$Id:\" at the front */\n+\n \t\t\t/* discard up to but not including the closing $ */\n \t\t\tunsigned long rem = size - 3;\n+\t\t\t/* Point at first byte after the \":\" */\n \t\t\tcp = src + 3;\n+\t\t\t/*\n+\t\t\t * Throw away characters until either\n+\t\t\t *  - we reach a \"$\"\n+\t\t\t *  - we run out of bytes (rem == 0)\n+\t\t\t */\n \t\t\tdo {\n-\t\t\t\tch = *cp++;\n+\t\t\t\tch = *cp;\n \t\t\t\tif (ch == '$')\n \t\t\t\t\tbreak;\n+\t\t\t\tcp++;\n \t\t\t\trem--;\n \t\t\t} while (rem);\n+\t\t\t/* If the above finished because it ran out of characters, then\n+\t\t\t * this is an incomplete keyword, so don't run the expansion */\n \t\t\tif (!rem)\n \t\t\t\tcontinue;\n-\t\t\tsize -= (cp - src);\n \t\t} else if (src[2] == '$')\n \t\t\tcp = src + 2;\n \t\telse\n+\t\t\t/* Anything other than \"$Id:XXX$\" or $Id$ and we skip the\n+\t\t\t * expansion */\n \t\t\tcontinue;\n \n+\t\t/* cp is now pointing at the last $ of the keyword */\n+\n \t\tmemcpy(dst, \"Id: \", 4);\n \t\tdst += 4;\n \t\tmemcpy(dst, sha1_to_hex(sha1), 40);\n \t\tdst += 40;\n \t\t*dst++ = ' ';\n+\n+\t\t/* Adjust for the characters we've discarded */\n \t\tsize -= (cp - src);\n \t\tsrc = cp;\n+\n+\t\t/* Copy the final \"$\" */\n \t\t*dst++ = *src++;\n \t\tsize--;\n \t}\n-- \n1.5.2.763.g8c5e-dirty\n"},{"id":"43233","messageId":"20070525105836.GG6667@always.joy.eth.net","threadId":"8312","inReplyTo":"200705251150.09439.andyparkins@gmail.com","subject":"Re: [PATCH] Fix mishandling of $Id$ expanded in the repository copy in convert.c","fromName":"Joshua N Pritikin","fromEmail":"jpritikin@pobox.com","sentAt":"2007-05-25T10:58:36Z","receivedAt":"2007-05-25T10:58:36Z","isPatch":true,"sender":{"key":"jpritikin@pobox.com","avatar":"https://gravatar.com/avatar/3f2561fdd7efac4e127dc65ac7e06f044069c115dcc94d0ac540f4126d47759d?d=mp&s=160"},"body":"On Fri, May 25, 2007 at 11:50:08AM +0100, Andy Parkins wrote:\n> +\t\t\t/*\n> +\t\t\t * Throw away characters until either\n> +\t\t\t *  - we reach a \"$\"\n> +\t\t\t *  - we run out of bytes (rem == 0)\n> +\t\t\t */\n>  \t\t\tdo {\n> -\t\t\t\tch = *cp++;\n> +\t\t\t\tch = *cp;\n>  \t\t\t\tif (ch == '$')\n>  \t\t\t\t\tbreak;\n> +\t\t\t\tcp++;\n>  \t\t\t\trem--;\n>  \t\t\t} while (rem);\n\nCan this loop throw away newlines? Removing newlines seems like a bad \nidea.\n"},{"id":"43244","messageId":"200705251412.06196.andyparkins@gmail.com","threadId":"8312","inReplyTo":"20070525105836.GG6667@always.joy.eth.net","subject":"Re: [PATCH] Fix mishandling of $Id$ expanded in the repository copy in convert.c","fromName":"Andy Parkins","fromEmail":"andyparkins@gmail.com","sentAt":"2007-05-25T13:12:02Z","receivedAt":"2007-05-25T13:12:02Z","isPatch":true,"sender":{"key":"andyparkins@gmail.com","avatar":null},"body":"On Friday 2007 May 25, Joshua N Pritikin wrote:\n\n> Can this loop throw away newlines? Removing newlines seems like a bad\n> idea.\n\nIt can and does.  My patch is only a bug fix though, not a change in \nfunctionality.\n\nIt's probably not likely that they will appear inside the $Id: XXXXX $ \nexpansion, and even less likely that that expansion will make it into the \nrepository copy; however it's easily fixed ...  patch to follow.  \n\n\nAndy\n\n-- \nDr Andy Parkins, M Eng (hons), MIET\nandyparkins@gmail.com\n"},{"id":"43245","messageId":"200705251413.42389.andyparkins@gmail.com","threadId":"8312","inReplyTo":"200705251412.06196.andyparkins@gmail.com","subject":"[PATCH] Don't allow newlines to occur in $Id:$ collapse","fromName":"Andy Parkins","fromEmail":"andyparkins@gmail.com","sentAt":"2007-05-25T13:13:42Z","receivedAt":"2007-05-25T13:13:42Z","isPatch":true,"sender":{"key":"andyparkins@gmail.com","avatar":null},"body":"If a newline ever made it into an repository-side expanded $Id$ field,\nthe keyword would still be detected as a keyword and collapsed, before\nrexpansion, e.g.\n\n $Id: all of this text would be removed, even if there\n were a newline in the middle of it$\n\nThis patch catches newlines in this case and abandons treating this as a\nkeyword expansion, this text would be left untouched in the working\ncheckout.\n\nSigned-off-by: Andy Parkins <andyparkins@gmail.com>\n---\n convert.c |    4 +++-\n 1 files changed, 3 insertions(+), 1 deletions(-)\n\ndiff --git a/convert.c b/convert.c\nindex 3c44e3d..051366c 100644\n--- a/convert.c\n+++ b/convert.c\n@@ -547,12 +547,14 @@ static char *ident_to_worktree(const char *path, const char *src, unsigned long\n \t\t\t\tch = *cp;\n \t\t\t\tif (ch == '$')\n \t\t\t\t\tbreak;\n+\t\t\t\tif (ch == '\\n')\n+\t\t\t\t\tbreak;\n \t\t\t\tcp++;\n \t\t\t\trem--;\n \t\t\t} while (rem);\n \t\t\t/* If the above finished because it ran out of characters, then\n \t\t\t * this is an incomplete keyword, so don't run the expansion */\n-\t\t\tif (!rem)\n+\t\t\tif (!rem || ch == '\\n')\n \t\t\t\tcontinue;\n \t\t} else if (src[2] == '$')\n \t\t\tcp = src + 2;\n-- \n1.5.2.763.g8c5e-dirty\n"},{"id":"43246","messageId":"20070525132800.GH6667@always.joy.eth.net","threadId":"8312","inReplyTo":"200705251413.42389.andyparkins@gmail.com","subject":"Re: [PATCH] Don't allow newlines to occur in $Id:$ collapse","fromName":"Joshua N Pritikin","fromEmail":"jpritikin@pobox.com","sentAt":"2007-05-25T13:28:00Z","receivedAt":"2007-05-25T13:28:00Z","isPatch":true,"sender":{"key":"jpritikin@pobox.com","avatar":"https://gravatar.com/avatar/3f2561fdd7efac4e127dc65ac7e06f044069c115dcc94d0ac540f4126d47759d?d=mp&s=160"},"body":"On Fri, May 25, 2007 at 02:13:42PM +0100, Andy Parkins wrote:\n> If a newline ever made it into an repository-side expanded $Id$ field,\n> the keyword would still be detected as a keyword and collapsed, before\n> rexpansion, e.g.\n> \n>  $Id: all of this text would be removed, even if there\n>  were a newline in the middle of it$\n> \n> This patch catches newlines in this case and abandons treating this as a\n> keyword expansion, this text would be left untouched in the working\n> checkout.\n\nThat's better but I would error out instead of silently ignoring it.\nYour choice.\n"},{"id":"43249","messageId":"alpine.LFD.0.99.0705250942500.3366@xanadu.home","threadId":"8312","inReplyTo":"20070525132800.GH6667@always.joy.eth.net","subject":"Re: [PATCH] Don't allow newlines to occur in $Id:$ collapse","fromName":"Nicolas Pitre","fromEmail":"nico@cam.org","sentAt":"2007-05-25T13:47:04Z","receivedAt":"2007-05-25T13:47:04Z","isPatch":true,"sender":{"key":"nico@fluxnic.net","avatar":"https://avatars.githubusercontent.com/u/702790?v=4"},"body":"On Fri, 25 May 2007, Joshua N Pritikin wrote:\n\n> On Fri, May 25, 2007 at 02:13:42PM +0100, Andy Parkins wrote:\n> > If a newline ever made it into an repository-side expanded $Id$ field,\n> > the keyword would still be detected as a keyword and collapsed, before\n> > rexpansion, e.g.\n> > \n> >  $Id: all of this text would be removed, even if there\n> >  were a newline in the middle of it$\n> > \n> > This patch catches newlines in this case and abandons treating this as a\n> > keyword expansion, this text would be left untouched in the working\n> > checkout.\n> \n> That's better but I would error out instead of silently ignoring it.\n> Your choice.\n\nErroring out in such a case would simply make the system too obnoxious.\n\nI don't think it is really worth aborting a commit just because you have \na bad $Id:$ in one of your file.\n\n\nNicolas\n"},{"id":"43250","messageId":"200705251451.00999.andyparkins@gmail.com","threadId":"8312","inReplyTo":"20070525132800.GH6667@always.joy.eth.net","subject":"Re: [PATCH] Don't allow newlines to occur in $Id:$ collapse","fromName":"Andy Parkins","fromEmail":"andyparkins@gmail.com","sentAt":"2007-05-25T13:50:53Z","receivedAt":"2007-05-25T13:50:53Z","isPatch":true,"sender":{"key":"andyparkins@gmail.com","avatar":null},"body":"On Friday 2007 May 25, Joshua N Pritikin wrote:\n\n> That's better but I would error out instead of silently ignoring it.\n> Your choice.\n\nWe can't error out on checking a file out - that file is in the repository \nalready, if it's got problems - so be it, it's got to be possible to check it \nout.\n\nOne could even argue that it's not actually an error, if we define keywords to \nbe such that they are not allowed to contain newlines, then the fact that \nsomeone has written \"$Id:\" in their file, with no closing \"$\" just means that \nit's not a keyword; and like every other non-keyword bit of data in the file \nit should be left untouched.\n\n\n\nAndy\n\n-- \nDr Andy Parkins, M Eng (hons), MIET\nandyparkins@gmail.com\n"},{"id":"43257","messageId":"20070525144058.GB5226@always.joy.eth.net","threadId":"8312","inReplyTo":"200705251451.00999.andyparkins@gmail.com","subject":"Re: [PATCH] Don't allow newlines to occur in $Id:$ collapse","fromName":"Joshua N Pritikin","fromEmail":"jpritikin@pobox.com","sentAt":"2007-05-25T14:40:58Z","receivedAt":"2007-05-25T14:40:58Z","isPatch":true,"sender":{"key":"jpritikin@pobox.com","avatar":"https://gravatar.com/avatar/3f2561fdd7efac4e127dc65ac7e06f044069c115dcc94d0ac540f4126d47759d?d=mp&s=160"},"body":"On Fri, May 25, 2007 at 02:50:53PM +0100, Andy Parkins wrote:\n> One could even argue that it's not actually an error, if we define keywords to \n> be such that they are not allowed to contain newlines, then the fact that \n> someone has written \"$Id:\" in their file, with no closing \"$\" just means that \n> it's not a keyword; and like every other non-keyword bit of data in the file \n> it should be left untouched.\n\nAh, so the original patch, before I started kibitzing, was correct.\n\nSorry for the noise.\n"},{"id":"43334","messageId":"7vlkfcm2eu.fsf@assigned-by-dhcp.cox.net","threadId":"8312","inReplyTo":"200705251150.09439.andyparkins@gmail.com","subject":"Re: [PATCH] Fix mishandling of $Id$ expanded in the repository copy in convert.c","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-05-26T08:09:45Z","receivedAt":"2007-05-26T08:09:45Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Andy Parkins <andyparkins@gmail.com> writes:\n\n> I've included the comments I wrote while debugging in this patch, which\n> I'm sure will annoy you, because you'd rather the fix and the comments\n> separately.  I'll supply that if you wish - just holler.\n\nActually I like well commented code, although some of your\ncomments feel a tad too much at places.  For example,\n\n>  \tfor (dst = buf; size; size--) {\n>  \t\tconst char *cp;\n> +\t\t/* Fetch next source character, move the pointer on */\n>  \t\tchar ch = *src++;\n> +\t\t/* Copy the current character to the destination */\n>  \t\t*dst++ = ch;\n\nThese are too much.\n\n> +\t\t/* If the current character is \"$\" or there are less than three\n> +\t\t * remaining bytes or the two bytes following this one are not\n> +\t\t * \"Id\", then simply read the next character */\n>  \t\tif ((ch != '$') || (size < 3) || memcmp(\"Id\", src, 2))\n>  \t\t\tcontinue;\n> +\t\t/*\n> +\t\t * Here when\n> +\t\t *  - There are more than 2 bytes remaining\n> +\t\t *  - The current three bytes are \"$Id$\"\n> +\t\t * with\n> +\t\t *  - ch == \"$\"\n> +\t\t *  - src[0] == \"I\"\n> +\t\t */\n\nBut this is very good, if you fix it to read the current 3 are\n\"$Id\" ;-).\n\n> +\t\t/*\n> +\t\t * It's possible that an expanded Id has crept its way into the\n> +\t\t * repository, we cope with that by stripping the expansion out\n> +\t\t */\n\nSo are all the other comments.\n\nThanks for the fix.  It would be very nice for the patch to be\naccompanied with a new test to expose the bug and demonstrate\nthat the patch fixes it.\n"},{"id":"43338","messageId":"200705261012.50530.andyparkins@gmail.com","threadId":"8312","inReplyTo":"7vlkfcm2eu.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] Fix mishandling of $Id$ expanded in the repository copy in convert.c","fromName":"Andy Parkins","fromEmail":"andyparkins@gmail.com","sentAt":"2007-05-26T09:12:48Z","receivedAt":"2007-05-26T09:12:48Z","isPatch":true,"sender":{"key":"andyparkins@gmail.com","avatar":null},"body":"On Saturday 2007, May 26, Junio C Hamano wrote:\n\n> >  \tfor (dst = buf; size; size--) {\n> >  \t\tconst char *cp;\n> > +\t\t/* Fetch next source character, move the pointer on */\n> >  \t\tchar ch = *src++;\n> > +\t\t/* Copy the current character to the destination */\n> >  \t\t*dst++ = ch;\n>\n> These are too much.\n\nAbsolutely.  I always find when bug hunting though that I like to \ncomment every block, sometimes to excess, as reminder that I've \nunderstood what its doing.\n\nI should have done the final pass once I'd found it to clear up the \noverkill ones :-)\n\n> But this is very good, if you fix it to read the current 3 are\n> \"$Id\" ;-).\n\n\"and in the ability to count competition, Andy comes second... let's \nhave a big hand for our runner up\" :-)\n\n> Thanks for the fix.  It would be very nice for the patch to be\n> accompanied with a new test to expose the bug and demonstrate\n> that the patch fixes it.\n\nI had to jump through quite a few hoops to get the expanded $Id$ into a \nrepository (originally it was because I used an older version of git in \none place, and a newer one in another).\n\nI'll see what I can do to make a test case though.\n\n\nAndy\n\n-- \nDr Andy Parkins, M Eng (hons), MIET\nandyparkins@gmail.com\n"},{"id":"43358","messageId":"7vveefiex4.fsf@assigned-by-dhcp.cox.net","threadId":"8312","inReplyTo":"200705261012.50530.andyparkins@gmail.com","subject":"Re: [PATCH] Fix mishandling of $Id$ expanded in the repository copy in convert.c","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-05-26T19:05:27Z","receivedAt":"2007-05-26T19:05:27Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Andy Parkins <andyparkins@gmail.com> writes:\n\n> I had to jump through quite a few hoops to get the expanded $Id$ into a \n> repository (originally it was because I used an older version of git in \n> one place, and a newer one in another).\n>\n> I'll see what I can do to make a test case though.\n\nWouldn't it be sufficient to:\n\n (1) prepare a file with \"$Id$\", use ident in .gitattributes,\n     check it in and commit;\n\n (2) remove it from the working tree, check it out with\n     \"checkout -f\";\n\n (3) temorarily move away .gitattributes, modify the file, and\n     check it in;\n\n (4) move .gitattributes back into its place, and commit.\n"},{"id":"43404","messageId":"200705271150.50147.andyparkins@gmail.com","threadId":"8312","inReplyTo":"7vveefiex4.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] Fix mishandling of $Id$ expanded in the repository copy in convert.c","fromName":"Andy Parkins","fromEmail":"andyparkins@gmail.com","sentAt":"2007-05-27T10:50:48Z","receivedAt":"2007-05-27T10:50:48Z","isPatch":true,"sender":{"key":"andyparkins@gmail.com","avatar":null},"body":"On Saturday 2007, May 26, Junio C Hamano wrote:\n\n> Wouldn't it be sufficient to:\n>\n>  (1) prepare a file with \"$Id$\", use ident in .gitattributes,\n>      check it in and commit;\n>\n>  (2) remove it from the working tree, check it out with\n>      \"checkout -f\";\n>\n>  (3) temorarily move away .gitattributes, modify the file, and\n>      check it in;\n>\n>  (4) move .gitattributes back into its place, and commit.\n\nI'm glad to have you confirm that.  I wasn't sure if git would do \nsomething clever and reading the .gitattributes from the same commit as \nthe file for which is being checked out.\n\nIf the above would work, then even simpler:\n\n (1) Commit a file with $Id: blah blah blah $ in it.\n (2) Add a .gitattributes with ident\n (3) Check out.\n\nPatch to follow.\n\n\nAndy\n\n-- \nDr Andy Parkins, M Eng (hons), MIET\nandyparkins@gmail.com\n"},{"id":"43406","messageId":"200705271152.11962.andyparkins@gmail.com","threadId":"8312","inReplyTo":"200705271150.50147.andyparkins@gmail.com","subject":"[PATCH] Add test case for $Id$ expanded in the repository","fromName":"Andy Parkins","fromEmail":"andyparkins@gmail.com","sentAt":"2007-05-27T10:52:11Z","receivedAt":"2007-05-27T10:52:11Z","isPatch":true,"sender":{"key":"andyparkins@gmail.com","avatar":null},"body":"This test case would have caught the bug fixed by revision\nc23290d5.\n\nIt puts various forms of $Id$ into a file in the repository,\nwithout allowing git to collapse them to uniformity.  Then enables the\n$Id$ expansion on checkout, and checks that what is checked out has\ncoped with the various forms.\n\nSigned-off-by: Andy Parkins <andyparkins@gmail.com>\n---\n t/t0021-conversion.sh |   36 ++++++++++++++++++++++++++++++++++++\n 1 files changed, 36 insertions(+), 0 deletions(-)\n\ndiff --git a/t/t0021-conversion.sh b/t/t0021-conversion.sh\nindex 6c26fd8..a839f4e 100755\n--- a/t/t0021-conversion.sh\n+++ b/t/t0021-conversion.sh\n@@ -45,4 +45,40 @@ test_expect_success check '\n \ttest \"z$id\" = \"z$embedded\"\n '\n \n+# If an expanded ident ever gets into the repository, we want to make sure that\n+# it is collapsed before being expanded again on checkout\n+test_expect_success expanded_in_repo '\n+\t{\n+\t\techo \"File with expanded keywords\"\n+\t\techo \"\\$Id\\$\"\n+\t\techo \"\\$Id:\\$\"\n+\t\techo \"\\$Id: 0000000000000000000000000000000000000000 \\$\"\n+\t\techo \"\\$Id: NoSpaceAtEnd\\$\"\n+\t\techo \"\\$Id:NoSpaceAtFront \\$\"\n+\t\techo \"\\$Id:NoSpaceAtEitherEnd\\$\"\n+\t\techo \"\\$Id: NoTerminatingSymbol\"\n+\t} > expanded-keywords &&\n+\n+\t{\n+\t\techo \"File with expanded keywords\"\n+\t\techo \"\\$Id: 4f21723e7b15065df7de95bd46c8ba6fb1818f4c \\$\"\n+\t\techo \"\\$Id: 4f21723e7b15065df7de95bd46c8ba6fb1818f4c \\$\"\n+\t\techo \"\\$Id: 4f21723e7b15065df7de95bd46c8ba6fb1818f4c \\$\"\n+\t\techo \"\\$Id: 4f21723e7b15065df7de95bd46c8ba6fb1818f4c \\$\"\n+\t\techo \"\\$Id: 4f21723e7b15065df7de95bd46c8ba6fb1818f4c \\$\"\n+\t\techo \"\\$Id: 4f21723e7b15065df7de95bd46c8ba6fb1818f4c \\$\"\n+\t\techo \"\\$Id: NoTerminatingSymbol\"\n+\t} > expected-output &&\n+\n+\tgit add expanded-keywords &&\n+\tgit commit -m \"File with keywords expanded\" &&\n+\n+\techo \"expanded-keywords ident\" >> .gitattributes &&\n+\n+\trm -f expanded-keywords &&\n+\tgit checkout -- expanded-keywords &&\n+\tcat expanded-keywords &&\n+\tcmp expanded-keywords expected-output\n+'\n+\n test_done\n-- \n1.5.2.86.g99b5-dirty\n"}]}