↑ arvau

Stack Overflow / Git filter and sed fight over `\$`

I am trying to write a git filter using sed

[filter "revisioner"]
        smudge = sed -e "s/\\\$Revision\\\$/\$Revision: $(GIT_EXEC_PATH=/usr/lib/git-core/ git describe --tag svn)\$/g"
        clean = sed -e "s/\\\$Revision: [^$]*\\\$/\$Revision\$/g"

For sed to do the correct thing it wants to have an odd number of \ to escape the $, however git complains about an odd number of \ with:

fatal: bad config line 2 in file .git/../.gitconfig

I don't really want to move my sed script into an external command because it is yet an other moving part my users need to setup on their system, before using my repository.

Any advice?

Stack Overflow / Git filter and sed fight over `\$`

sed does not need an odd number of backslashes. sed needs to receive, from whatever invokes sed, the two-character sequence backslash, dollar-sign.

Git itself turns two backslashes into one backslash.

The shell that Git invokes to run commands also turns two backslashes into one backslash, whenever it's lexically scanning through text that is not contained inside single quotes.

Therefore, if you start with four backslashes:

\\\\$

Git will turn each pair into two backslashes:

\\$

and the shell will then turn the pair of backslashes into one backslash:

\$

after which sed will see one backslash followed by one dollar sign.

Note that you must do this doubling at each point at which backslashes get interpreted. If there were one more level of interpretation applied you would need eight backslashes. This kind of thing gets ugly fast which is why people move these into additional scripts. If you provide your script as a committed file within the repository, that might be a lot easier to deal with...

Stack Overflow / Git filter and sed fight over `\$`

Getting escape parsing right is often a challenge, as you have to think about each program that will parse the command, and you have to think about them in order.

torek's answer has outlined the basic reasoning that applies to your case. I will add the following, because I think the end result is a bit more readable:

First, to get the underlying sed script to work at the command line - converting, in my test, $HI$ to $Hello, World$ - I used

sed -e 's/\$HI\$/$Hello World$/g'

The single-quotes prevent bash from messing with the escapes, so sed sees \$ and matches a literal $. Note that the output pattern doesn't require this escaping.

Then to put this in the .git/config file, double each backslash, giving

clean = sed -e 's/\\$HI\\$/$Hello World$/g'

This works in my tests; if it doesn't work for you, we may need more information to understand what's going on.