A git alias which makes it easy to dump the shell command used to create any arbitrary git alias.
Run this in your terminal:
git config --global alias.dump-alias $'!g(){ if [ -z $1 ]; then git config --list | grep "alias\."; else g|grep "alias\.$1="|perl -lpe \'($q,$d,$b)=map{chr}0x27,0x24,0x5c;($N,$B)=$_=~/(alias\.[^=]+)=(.+)/;$B=~s,$q,$b$q,g;$_=qq,git config --global $N $d$q$B$q,;\';fi;};g $1'
I golfed it down to try to fit within a tweet but it was still too long. Oh well.
You can read to the end of this gist to learn how that works.
Sometimes you have a really neat git
alias that you want to share with someone else.
Maybe you know where the alias is actually defined (repo scope? global ~/.gitconfig
?), so you open the file up in your editor to see what to send to your friend. Maybe it's been a while since you've actually used this alias, so you sort of forget what it's called. "notes
... notes something... grab-notes
? notes-fetch
?", you muse to yourself.
"Aha", you tell your friend. "Okay, just add this line to your
~/.gitconfig
and you'll have the new alias."[alias] fetch-notes = fetch origin 'refs/notes/*:refs/notes/*'
You feel a little badly that you didn't tell them to run a command in their shell to add that alias to git
, but you both move on with your lives. (Well, maybe you do. I know I don't!)
Maybe you don't want to tell friends to hand-edit their ~/.gitconfig
files. Well have I got great news for you!
Going back to our original example, you can simply run:
$ git dump-alias
# lists all your aliases
Handy!
How about finding aliases relating to notes
?
Just use grep
, silly!
But how can we turn that into something that you can copy to your clipboard and send to a friend?
Wonderful!
Once you install git dump-alias
, the first thing you'll want to do is share it with a friend. Oh dang, maybe it's not in your copy buffer any more, and you lost where you found it? No problem the alias can make itself shareable:
$ git dump-alias dump-alias | pbcopy # osx
$ git dump-alias dump-alias | xclip -sel clipboard # linux
Obviously at this point our original problem of how to share the fetch-notes
alias is solved!
$ git dump-alias fetch-notes | tee /dev/tty | pbcopy
git config --global alias.fetch-notes $'fetch origin \'refs/notes/*:refs/notes/*\''
Here was the original entry I hand-wrote into my ~/.gitconfig
.
[alias]
dump-alias-long = "!git_share_alias () { target=$1; if [ -z $target ]; then git config --list | grep \"alias\\.\"; return; fi; val=$(git config --list | grep \"alias\\.${target}=\"); echo \"$val\" | perl -lne '$q=chr(0x27);$d=chr(0x24);$b=chr(0x5c);($name,$body)=$_=~m,alias\\.([^=]+)=(.+),;$body=~s,$q,$b$q,g;print qq,git config --global alias.$name $d$q$body$q,;';};git_share_alias $1"
We can pull that out into a shell script. Note that the examples below use #!/usr/bin/env sh
rather than #!/usr/bin/env bash
. This isn't a mistake on my part, nor is it some weird love for sh
(...I do admit to a weird love for bash
). Any git
aliases which start with !
will be run under /bin/sh
, so we seek it out here.
This first version of just sticking in whitespace isn't very useful, but click to look ↷
There's a lot of extra escaping that we've inherited, since we need to stick this script into a "double-quoted-string"
when making our git
alias.
Here it is without the extra escaping cleaned up; this won't work correctly if you run it.
#!/usr/bin/env sh
git_share_alias () {
target=$1;
if [ -z $target ]; then
git config --list | grep \"alias\\.\";
return;
fi;
val=$(git config --list | grep \"alias\\.${target}=\");
echo \"$val\" | perl -lne '
$q=chr(0x27);
$d=chr(0x24);
$b=chr(0x5c);
($name,$body) = $_ =~ m,alias\\.([^=]+)=(.+),;
$body =~ s,$q,$b$q,g;
print qq,git config --global alias.$name $d$q$body$q,;
';
};
git_share_alias $1
Here's the first runnable extraction of git dump-alias
into a shell script:
#!/usr/bin/env sh
git_share_alias () { # 01
target=$1; # 02
if [ -z $target ]; then # 04
git config --list | grep "alias\."; # 05
return; # 06
fi; # 07
val=$(git config --list | grep "alias\.${target}="); # 08
echo "$val" | perl -lne ' # 10
$q=chr(0x27); # 11
$d=chr(0x24); # 12
$b=chr(0x5c); # 13
($name,$body) = $_ =~ m,alias\.([^=]+)=(.+),; # 14
$body =~ s,$q,$b$q,g; # 15
print qq,git config --global alias.$name $d$q$body$q,; # 16
'; # 17
}; # 18
git_share_alias $1 # 20
I've put line numbers as comments on the right so we can still see a little bit of syntax highlighting.
-
Line 1 of the script introduces a function,
git_share_alias
, which is terminated at line 18. On line 20, we call the function, passing in whatever argument was given. -
Lines 2-7 check whether we were called as
git dump-alias
or asgit dump-alias foofoo
. If the former case, then we just list all the aliases and exit. -
Line 8 looks at the list of
git
aliases, finds the one you asked for, and uses that as the "val
" to work on. -
Lines 10-18 are a pretty terrible little Perl script, which takes a line like this
alias.fetch-notes=fetch origin 'refs/notes/*:refs/notes/*'
and turns it into this:
git config --global alias.fetch-notes $'fetch origin \'refs/notes/*:refs/notes/*\''
Looking at the input and the output, you'd probably guess that properly escaping things for the shell is probably the biggest thing that Perl script is doing, and indeed you'd be right.
-
Here's the inner Perl script with some syntax highlighting:
$q=chr(0x27); # 11 $d=chr(0x24); # 12 $b=chr(0x5c); # 13 ($name,$body) = $_ =~ m,alias\.([^=]+)=(.+),; # 14 $body =~ s,$q,$b$q,g; # 15 print qq,git config --global alias.$name $d$q$body$q,; # 16
- lines 11, 12, and 13 declare some variables,
$q, $d, $b
with some tricky-to-escape characters: respectively,' $ \
. (single-quote, dollar-sign, and backslash). Using these variables means thatgit dump-alias
can successfully make itself shareable: most notably$b
comes to our aid there. - line 14 takes a line like this:
alias.run=!exec $@
and sets the variable$name = 'run'
, and$body = '!exec $@'
. - line 15 is responsible for escaping
'
within the alias body by making it into\'
. - line 16 prints the
git config
line, using$'...'
syntax for strings
- lines 11, 12, and 13 declare some variables,
The golfed version of git dump-alias
uses a few Perl tricks to reduce character count a bit. It also uses a silly recursion to jump between branches:
g() {
if [ -z $1 ]; then # If no arg given...
git config --list | grep "alias\." # ... list all the aliases.
else # Otherwise
git config --list | grep "alias\.$1=" # ... find the alias we want
... # ... and whack it with Perl
fi
}
This gets simplified in the golfed version:
g() {
if [ -z $1 ]; then # If no arg given...
git config --list | grep "alias\." # ... list all the aliases.
else # Otherwise
g | grep "alias\.$1=" # ... find the alias we want
... # ... and whack it with Perl
fi
}
Ahh, the savings!
This might seem like a problem that I invented just to solve it. But honestly, I do run into this periodically. I tinker with git
aliases a lot, and periodically want to make it easy to share them with colleagues.
Some of my colleagues aren't software developers but still use git
on an infrequent basis. Small improvements in their workflow by way of git
plugins or aliase go a long way towards making hard things easier.