在.gitconfig
中,我创建了这种类型的别名:
[alias]
something = "!eval `ssh-agent -s` && ssh-add ~/.ssh/id_acub && git config --global user.email <email> && git config --global user.name <user.name>"
此时,运行git something
会正确显示信息,但它不能像我预期的那样工作。
如果我在控制台中键入一行:
eval `ssh-agent -s` && ssh-add ~/.ssh/id_acub && git config --global user.email <email> && git config --global user.name <user.name>
一切都运作良好。
别名有什么问题?
让我们从一个比ssh-agent -s
更简单的例子开始:
$ sh -c 'FOO=bar; echo FOO is now $FOO'
FOO is now bar
$ echo FOO is now $FOO
FOO is now
为什么这不起作用?当我设置一些其他shell实例的FOO
变量时,为什么我的登录shell的FOO
变量没有设置?
好吧,我希望答案是显而易见的:我的shell没有设置FOO
。我设置了一些其他shell的FOO
变量。我告诉其他shell打印它,它已经设置好了。然后那个其他shell完成 - 退出 - 并将控制权交还给我的登录shell,当我告诉我的登录shell打印我的登录shell的FOO
时,它没有设置。
ssh-agent -s
所做的是打印出一些作业。我们试试看:
$ sh -c 'echo FOO=bar'
FOO=bar
$ echo FOO is now $FOO
FOO is now
当然,这也不起作用,因为我只是打印了一些说明。没有人遵循这些指示。
那么让我们再试一次:
$ eval `sh -c 'echo FOO=bar'`
$ echo FOO is now $FOO
FOO is now bar
这一次,我:
$FOO
的设置因为我的shell后面的说明改变了我的shell中的内容,现在我的shell的FOO已经设置好了。
ssh-agent
的设计是相同的:它做了一些工作,然后 - 在各种模式,包括与-s
打印出一些shell的说明。您需要使用eval
来使该shell遵循这些说明。
只要遵循这些说明的shell是你的shell,这一切都很好,只做你想要的。但是当你制作一个Git别名时,Git本身会运行一个新的,不同的shell。该shell遵循说明,一旦完成,您的shell不受影响。
这意味着你不能用Git别名做你想做的事。您必须使用会影响shell的别名或函数,而不是会启动新shell,影响它,然后让它终止并让这些效果消失的别名或函数。
请注意,某些更改(如git config --global
)存储在文件中,而不是存储在shell中。当壳消失时,这些变化不会蒸发。这是一个关键的区别:文件系统状态存储在各个进程之外。如果ssh-agent
所做的一切都存储在文件系统状态中,那么可以使它工作,但ssh-agent
所做的一些事情并没有存储在那里。