Skip to content

Instantly share code, notes, and snippets.

@belden
Last active August 18, 2023 23:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save belden/483130172f23e2a66ba22982c6835941 to your computer and use it in GitHub Desktop.
Save belden/483130172f23e2a66ba22982c6835941 to your computer and use it in GitHub Desktop.
A git alias to make sharing git aliases easier

git dump-alias

A git alias which makes it easy to dump the shell command used to create any arbitrary git alias.

Installing git dump-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.

Motivation

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.

image

"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!

Using git dump-alias

Going back to our original example, you can simply run:

List all your git aliases

$ git dump-alias
# lists all your aliases

Handy!

Searching your git aliases

How about finding aliases relating to notes?

Just use grep, silly!

Sharing a git alias

But how can we turn that into something that you can copy to your clipboard and send to a friend?

Wonderful!

Fun with git dump-alias

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/*\''

git dump-alias degolfed

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.

  1. 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.

  2. Lines 2-7 check whether we were called as git dump-alias or as git dump-alias foofoo. If the former case, then we just list all the aliases and exit.

  3. Line 8 looks at the list of git aliases, finds the one you asked for, and uses that as the "val" to work on.

  4. 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.

  5. 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 that git 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

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!

Apology

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment