Search code examples
gitsshalias

git alias ssh-add .gitconfig


In .gitconfig I created an alias of this type:

[alias]
    something = "!eval `ssh-agent -s` && ssh-add ~/.ssh/id_acub && git config --global user.email <email> && git config --global user.name <user.name>"

At this point, running git something displays the information correctly, but it does not work as I expected.

If I type a line in the console:

eval `ssh-agent -s` && ssh-add ~/.ssh/id_acub && git config --global user.email <email> && git config --global user.name <user.name>

then everything works well.

What is wrong in the alias?


Solution

  • Let's start with a simpler example than ssh-agent -s:

    $ sh -c 'FOO=bar; echo FOO is now $FOO'
    FOO is now bar
    $ echo FOO is now $FOO
    FOO is now
    

    Why didn't this work? Why is my login shell's FOO variable not set, when I set some other shell instance's FOO variable?

    Well, I hope the answer is obvious: my shell does not have FOO set. I set some other shell's FOO variable. I told that other shell to print it, and it was set. Then that other shell finished—exited—and gave control back to my login shell, and when I told my login shell to print my login shell's FOO, it was not set.

    What ssh-agent -s does is print out some set of assignments. Let's try that:

    $ sh -c 'echo FOO=bar'
    FOO=bar
    $ echo FOO is now $FOO
    FOO is now
    

    This did not work either, of course, because I simply printed some instructions. No one followed those instructions.

    So let's try one more thing:

    $ eval `sh -c 'echo FOO=bar'`
    $ echo FOO is now $FOO
    FOO is now bar
    

    This time, I:

    • had another command print some instructions
    • told my shell to follow those instructions
    • and then had my shell print out my shell's setting for $FOO

    and since the instructions I had my shell follow changed something in my shell, now my shell's FOO is set.

    The design for ssh-agent is the same: it does some work, then—in various modes including with -sprints out instructions for some shell to follow. You need the eval to make that shell follow those instructions.

    That's all good and does just what you want as long as the shell that's following these instructions is your shell. But when you make a Git alias, Git itself runs a new, different shell. That shell follows the instructions, and when it is done, your shell is unaffected.

    What this means is that you cannot do what you want with a Git alias. You must use an alias or function that will affect your shell, not one that will start a new shell, affect it, and then have it terminate and have those effects evaporate.

    Note that some changes, such as git config --global, are stored in files, rather than in your shell. These changes don't evaporate when the shell vanishes. That's a key difference: file system state is stored outside individual processes. If everything ssh-agent did were stored in file system state, it could be made to work, but some of the things ssh-agent does are not stored there.