Git config alias escaping
Asked Answered
I

3

12

I'm trying to write a git alias that removes from the commit messages the string "[ci skip]" (placed at the end of the message), but I'm having problem with escaping. The alias takes all the commit from the one passed as argument to HEAD.

If I run the following command:

git filter-branch -f --msg-filter "sed -e \"s/\[ci skip\]$//g\"" master..HEAD

it works as expected. Anyway if I create the following alias:

unwip = !sh -c 'git filter-branch -f --msg-filter \"sed -e \\\"s/\[ci skip\]$//g\\\"\" $0..HEAD'

and I run git unwip master it complains about bad config, but I expect it to behave as the previous commads. How can I fix this?

Ignominious answered 27/6, 2016 at 14:59 Comment(2)
Just guessing: Maybe also shield the backslash escaping the squarebrackets by additional escaped backslash like so \\\[ci skip\\\] ?Wisp
@Wisp not working unfortunately.Ignominious
S
3

EDIT This solution doesn't work in all cases. Here is a correct solution which works in all cases.

I'm using bash as a command-line and the following alias worked for me:

unwip = "!f() { git filter-branch --msg-filter 'sed -e "s/[ci skip/]$/g"' $1..HEAD ; }; f"

The only downside is that you're specifying the interpreter and always using sh. In my case I'm relying on user's shell. Though I don't believe it will be a problem in any of major shells as we're doing just basic stuff.

Sombrous answered 28/6, 2016 at 8:56 Comment(1)
This is an incorrect example - the " after sed -e actually ends the first quoted string. The coloured syntax highlighting demonstrates this. You're lucky that the ; characters are inside a second quoted string, otherwise git would treat ; as a comment character and ignore the rest of the line. See my answer for a correct solution.Tovatovar
T
30

Generic solution

Getting a git alias to pass the parser correctly can be a mind-boggling set of \\\\" noise, so I created two aliases:

# Quote / unquote a sh command, converting it to / from a git alias string
quote-string = "!read -r l; printf \\\"!; printf %s \"$l\" | sed 's/\\([\\\"]\\)/\\\\\\1/g'; printf \" #\\\"\\n\" #"
quote-string-undo = "!read -r l; printf %s \"$l\" | sed 's/\\\\\\([\\\"]\\)/\\1/g'; printf \"\\n\" #"

This allows you to convert anything you could type into sh+, eg:

$ echo '\"'

\"

Into a quoted string fit for an alias:

$ git quote-string
echo '\"'

"!echo '\\\"' #"

To quote a multi-line string, I wrote a longer script, which I suggest you run as:

git quote-string |sponge

Answering OP's specific issue

Using git quote-string on the OP's command, I get:

"!git filter-branch -f --msg-filter \"sed -e \\\"s/\\[ci skip\\]$//g\\\"\" master..HEAD #"

So, to use the OP's preferred alias name, under [alias] in ~/.gitconfig, add:

unwip = "!git filter-branch -f --msg-filter \"sed -e \\\"s/\\[ci skip\\]$//g\\\"\" master..HEAD #"

Debugging

Sometimes it's nice to see what is going on under the hood. Try this alias:

debug  = "!set -x; GIT_TRACE=2 GIT_CURL_VERBOSE=2 GIT_TRACE_PERFORMANCE=2 GIT_TRACE_PACK_ACCESS=2 GIT_TRACE_PACKET=2 GIT_TRACE_PACKFILE=2 GIT_TRACE_SETUP=2 GIT_TRACE_SHALLOW=2 git"

Just insert debug between git and whatever would usually follow, eg for the OP's question:

git debug unwip


+ git uses /bin/sh to execute aliases beginning with !*. To work around this, create a command line like: bash -c 'echo \\\"' and then pass this to git quote-string.

* See the "Debugging" heading for proof

Tovatovar answered 21/9, 2016 at 12:30 Comment(3)
Excellent! One issue is when I run git quote-string it outputs on multiple lines. Also you reference git shell-quote in your post, I presume you meant quote-string?Mccollough
Thanks. I fixed the terminology and copy/pasted from my working gitconfig for quote-string. I also added a script for multi-line aliases. Talk in chat if you still have multi-line output issues?Tovatovar
it appears the new aliases you pasted in are identical to the old ones. I set up a chatroom but was unable to invite you for some reason.Mccollough
S
3

EDIT This solution doesn't work in all cases. Here is a correct solution which works in all cases.

I'm using bash as a command-line and the following alias worked for me:

unwip = "!f() { git filter-branch --msg-filter 'sed -e "s/[ci skip/]$/g"' $1..HEAD ; }; f"

The only downside is that you're specifying the interpreter and always using sh. In my case I'm relying on user's shell. Though I don't believe it will be a problem in any of major shells as we're doing just basic stuff.

Sombrous answered 28/6, 2016 at 8:56 Comment(1)
This is an incorrect example - the " after sed -e actually ends the first quoted string. The coloured syntax highlighting demonstrates this. You're lucky that the ; characters are inside a second quoted string, otherwise git would treat ; as a comment character and ignore the rest of the line. See my answer for a correct solution.Tovatovar
R
0

git is suuuper helpful and just reports this error for any issue:

fatal: bad config line 19 in file /home/you/.gitconfig

Have fun trying a gazillion combinations of quotes and escapes to figure it out!

Eventually I resorted to reading the source code. Here's how it parses an alias:

alias = isspace* iskeychar+ spacetab* '=' value
isspace = ' ' | '\t' | '\v' | '\f' | '\r' (but not '\n')
spacetab = ' ' | '\t'
iskeychar = 'a'-'Z' | 'A'-'Z' | '0'-'9' | '-'
value = (quoted | unquoted)*
quoted = '"' not('\n')* '"'
unquoted = not('\n' | ';' | '#')*

Additionally:

  1. A comment is started by a ; or # outside of a quoted section and extends to the end of the line.
  2. The following backslash escapes work: \t, \n, \b, \", \\. They work exactly the same in quoted and unquoted sections. Other escape sequences cause an error.
  3. You can also ignore a newline by putting \ at the end of the line. Again this works exactly the same in quoted and unquoted sections.

Here's an example:

# !echo some ";"
    foo="!echo "some "\";\""

Of course, after all this I found it wasn't working because my key contained a _. What a git.

Rebato answered 24/5, 2023 at 10:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.