凭证存储
如果你使用的是 SSH 方式连接远端,并且设置了一个没有口令的密钥,这样就可以在不输入用户名和密码的情况下安全地传输数据。 然而,这对 HTTP 协议来说是不可能的 —— 每一个连接都是需要用户名和密码的。 这在使用双重认证的情况下会更麻烦,因为你需要输入一个随机生成并且毫无规律的 token 作为密码。
幸运的是,Git 拥有一个凭证系统来处理这个事情。 下面有一些 Git 的选项:
- 默认所有都不缓存。 每一次连接都会询问你的用户名和密码。
- “cache” 模式会将凭证存放在内存中一段时间。 密码永远不会被存储在磁盘中,并且在15分钟后从内存中清除。
- “store” 模式会将凭证用明文的形式存放在磁盘中,并且永不过期。 这意味着除非你修改了你在 Git 服务器上的密码,否则你永远不需要再次输入你的凭证信息。 这种方式的缺点是你的密码是用明文的方式存放在你的 home 目录下。
- 如果你使用的是 Mac,Git 还有一种 “osxkeychain” 模式,它会将凭证缓存到你系统用户的钥匙串中。 这种方式将凭证存放在磁盘中,并且永不过期,但是是被加密的,这种加密方式与存放 HTTPS 凭证以及 Safari 的自动填写是相同的。
- 如果你使用的是 Windows,你可以安装一个叫做 “winstore” 的辅助工具。 这和上面说的 “osxkeychain” 十分类似,但是是使用 Windows Credential Store 来控制敏感信息。 可以在https://gitcredentialstore.codeplex.com 下载。
你可以设置 Git 的配置来选择上述的一种方式
$
--file <path>
参数,可以自定义存放密码的文件路径(默认是~/.git-credentials
)。 “cache” 模式有 --timeout <seconds>
$
所有 配置列表中的辅助工具,它们会按自己的方式处理用户名和密码。 如果你在闪存上有一个凭证文件,但又希望在该闪存被拔出的情况下使用内存缓存来保存用户名密码,.gitconfig
[credential]
helper
=
store --file /mnt/thumbdrive/.git-credentials
helper = cache --timeout 30000
底层实现
这些是如何实现的呢? Git 凭证辅助工具系统的命令是 git credential
,这个命令接收一个参数,并通过标准输入获取更多的参数。举一个例子更容易理解。 我们假设已经配置好一个凭证辅助工具,这个辅助工具保存了 mygithost
$
git
credential
fill
protocol=https
host=mygithost
protocol=https
host=mygithost
username=bob
password=s3cre7
$
git
credential
fill
protocol=https
host=unknownhost
Username for 'https://unknownhost': bob
Password for 'https://bob@unknownhost':
protocol=https
host=unknownhost
username=bob
password=s3cre7
这是开始交互的命令。
Git-credential 接下来会等待标准输入。 我们提供我们所知道的信息:协议和主机名。
一个空行代表输入已经完成,凭证系统应该输出它所知道的信息。
接下来由 Git-credential 接管,并且将找到的信息打印到标准输出。
如果没有找到对应的凭证,Git 会询问用户的用户名和密码,我们将这些信息输入到在标准输出的地方(这个例子中是同一个控制台)。
凭证系统实际调用的程序和 Git 本身是分开的;具体是哪一个以及如何调用与 credential.helper
配置值 | 行为 |
| 执行 |
| 执行 |
| 执行 |
|
|
上面描述的辅助工具可以被称做 git-credential-cache
、git-credential-store
get
store
erase
对于 store
和 erase
两个行为是不需要返回数据的(Git 也会忽略掉)。 然而对于 get
,Git 对辅助工具的返回信息十分感兴趣。
如果辅助工具没有任何有用的信息,它可以直接退出而不需要输出任何东西,但如果它有这些信息,它在提供的信息后面增加它所拥有的信息。 这些输出会被视为一系列的赋值语句;每一个提供的数据都会将 Git 已有的数据替换掉。
这有一个和上面一样的例子,但是跳过了 git-credential 这一步,直接到 git-credential-store:
$
git
credential-store
--file
~/git.store
store
protocol=https
host=mygithost
username=bob
password=s3cre7
$
git
credential-store
--file
~/git.store
get
protocol=https
host=mygithost
username=bob
password=s3cre7
我们告诉 git-credential-store
去保存凭证:当访问 https://mygithost
现在我们取出这个凭证。 我们提供连接这部分的信息(https://mygithost
)以及一个空行。
git-credential-store
~/git.store
仅仅是一系列包含凭证信息URL组成的行。 osxkeychain
和 winstore
辅助工具使用它们后端存储的原生格式,而 cache
自定义凭证缓存
已经知道 git-credential-store
之类的是和 Git 是相互独立的程序,就不难理解 Git 凭证辅助工具可以是 任意get
- ;
store
- 和
erase
git-credential-store
- 凭证文件的路径一般是固定的,但我们应该允许用户传入一个自定义路径以防万一。
我们再一次使用 Ruby 来编写这个扩展,但只要 Git 能够执行最终的程序,任何语言都是可以的。 这是我们的凭证辅助工具的完整代码:
#!/usr/bin/env ruby
require
'optparse'
path
=
File
.
expand_path
'~/.git-credentials'
OptionParser
.
new
do
|
opts
|
opts
.
banner
=
'USAGE: git-credential-read-only [options] <action>'
opts
.
on
(
'-f'
,
'--file PATH'
,
'Specify path for backing store'
)
do
|
argpath
|
path
=
File
.
expand_path
argpath
end
end
.
parse!
exit
(
0
)
unless
ARGV
[
0
]
.
downcase
==
'get'
exit
(
0
)
unless
File
.
exists?
path
known
=
{
}
while
line
=
STDIN
.
gets
break
if
line
.
strip
==
''
k
,
v
=
line
.
strip
.
split
'='
,
2
known
[
k
]
=
v
end
File
.
readlines
(
path
)
.
each
do
|
fileline
|
prot
,
user
,
pass
,
host
=
fileline
.
scan
(
/
^(.*?):
\/
\/
(.*?):(.*?)@(.*)$
/
)
.
first
if
prot
==
known
[
'protocol'
]
and
host
==
known
[
'host'
]
then
puts
"
protocol=
#{
prot
}
"
puts
"
host=
#{
host
}
"
puts
"
username=
#{
user
}
"
puts
"
password=
#{
pass
}
"
exit
(
0
)
end
end
我们在这里解析命令行参数,允许用户指定输入文件,默认是 ~/.git-credentials
.
这个程序只有在接受到 get
这个循环从标准输入读取数据,直到读取到第一个空行。 输入的数据被保存到 known
这个循环读取存储文件中的内容,寻找匹配的行。 如果 known
我们把这个辅助工具保存为 git-credential-read-only
,放到我们的 PATH
$
=
protocol=https
host=mygithost
protocol=https
host=mygithost
username=bob
password=s3cre7
由于这个的名字是 “git-” 开头,所以我们可以在配置值中使用简便的语法:
$
read
正如你看到的,扩展这个系统是相当简单的,并且可以为你和你的团队解决一些常见问题。
总结
你已经接触了很多能够精确地操控提交和暂存区的高级工具。 当你碰到问题时,你应该可以很容易找出是哪个分支在什么时候由谁引入了它们。 如果你想在项目中使用子项目,你也已经知道如何来满足这些需求。 到此,你应该能毫无压力地在命令行中使用 Git 来完成日常中的大部分事情。