Git in Action
主要记录 git 在实际使用中碰到的问题,会逐步慢慢积累
# 高频问题与实践方案
# 新项目与远端同步
$ git init
$ git remote add origin git@e.coding.net:datamate/pujiangjiaoye.git # 添加远端仓库
$ git branch --set-upstream-to=origin/master # 设置分支对应关系
$ git push --set-upstream origin master # 设置push 对应的远程仓库,建立本地分支的上游
$ git remote -v # 查看分支
2
3
4
5
当我们尝试push的时候又会出现一个问题
error: 推送一些引用到 'git@e.coding.net:datamate/pujiangjiaoye.git' 失败
提示:更新被拒绝,因为您当前分支的最新提交落后于其对应的远程分支。
提示:再次推送前,先与远程变更合并(如 'git pull ...')。详见
提示:'git push --help' 中的 'Note about fast-forwards' 小节。
# 按照提示,尝试用git pull 合并,又失败
$ git pull
fatal: 拒绝合并无关的历史
2
3
4
5
6
7
8
原因是我们本地与远程的版本不一致,但是我们是新提交,怎么会出现这样的问题呢? 因为很多平台在我们创建项目的时候,自动帮我们创建了README.md文件,所以会有不一致的情况存在。git pull 失败的原因是,两个不一致的文件没有***共同祖先的历史***。
解决方法也很简单
git pull origin master --allow-unrelated-histories
# 修改上一次的提交
git 是分支管理的工具,我们的工作最好在一个 commit 中提交相应的代码,完成一个功能(如 bugfix 或者 feature_add )。但是有的时候,我们的提交经过 Code Review 之后,可能会被打回来。这样面对新的修改,不应该重复提交。
首先,我们可以先建立一个提交。面对第二个文件(这里我们给 a 新增了 Hello world again )
第一种选择,我们提交两个 commit
git add .
git commit -m "second commit"
2
第二种选择,我们直接修改上一次的修改(推荐)
工作流如下
git add .
git commit --amend # 代表直接在上一次的提交修改,这里会弹出上一次 commit 的信息。
git push -f # 由于我们只是改了本地,提交的时候使用 -f 强制推送一下
2
3
这里强烈建议,使用 --amend 的方法的时候,一定要检查 commit 的信息,确定不是把别人的给冲掉了。
# 服务器端强制同步远端 git 仓库
git fetch
git reset --hard origin/master # 要强制同步的分支!--hard 是丢弃分支的修改
git pull
Already up-to-date.
2
3
4
# 为什么修改了 .gitignore
文件,忽略项还是不起作用
新建的文件在git中会有缓存,如果某些文件已经被纳入了版本管理中,就算是在.gitignore中已经声明了忽略路径也是不起作用的,这时候我们就应该先把本地缓存删除,然后再进行git的push
git清除本地缓存命令如下:
git rm -r --cached .
git add .
2
下面附上我的 .gitignore
文件
# Windows:
Thumbs.db
ehthumbs.db
Desktop.ini
# MAC:
*.DS_Store
# Python:
*.py[cod]
*.so
*.egg
*.egg-info
dist
build
# My configurations:
*.log
*.log.*
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
如何使用 git merge
# 开发分支(dev)上的代码达到上线的标准后,要合并到 master 分支
git checkout dev
git pull
git checkout master
git pull
git merge dev
git push -u origin master
# 当master代码改动了,需要更新开发分支(dev)上的代码
git checkout master
git pull
git checkout dev # 这里默认dev 远端没有动
git merge master
git push -u origin dev
2
3
4
5
6
7
8
9
10
11
12
13
14
15
推荐一个 git merge
比较好的教程: merge:合并 commits (opens new window)
# 管理多个git账号
# 介绍
Git共有三个级别的 config
文件,分别是 system
、global
和 local
。global
的在$home\.gitconfig
,local的在仓库目录下的.git\config
。这三个级别都分别配置了用户信息,当git commit
时,会依次从local、global、system里读取用户信息。因此,我们利用local的配置来设置不同的用户信息
# 生成公钥
# 查看git账号,会按顺序读取local,global的用户信息
git config user.name
git config user.email
# 若是在空仓库中使用如下命令会出错,我们在仓库中使用--local,提前运行git init
git config --local -l
fatal: --local 只能在一个仓库内使用
# 更改本地账号
git config --local/--global user.name "Your name"
git config --local/--global user.email "Your email"
# 生成公钥
ssh-keygen -t rsa -C "your_email@example.com" -f ./
-t rsa 约定加密类型
-C 添加评论
-f 保存秘钥的地址(默认是 ~/.ssh/id_rsa)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
生成公钥的地方需要注意的是,生成的公钥会自动生成在~/.ssh/id_rsa
中,若是你有多个账号的话,无疑会把原先的秘钥覆盖。可以在后续弹出的提示中约定key保存的地址
Enter file in which to save the key (/Users/you/.ssh/id_rsa): [Press enter] // 推荐使用默认地址
Enter passphrase (empty for no passphrase): //此处点击 Enter 键即可,也可以填写密码,填写密码后每次使用 SSH 方式推送代码时都会要求输入密码,由于这个 Key 也不是用于军事目的,所以也无需设置密码。
2
也可以在ssh-keygen中约定好
ssh-keygen -t rsa -C "your_email@example.com" -f ~/.ssh/coding
-f 保存在yon下的.ssh/coding文件中
Your identification has been saved in /Users/ppsteven/.ssh/coding.
Your public key has been saved in /Users/ppsteven/.ssh/coding.pub.
The key fingerprint is:
SHA256:P6R/Fo70VkCyKsQsFXXX... XXX@outlook.com
2
3
4
5
6
7
.ssh 文件夹下也多了coding
和coding.pub
两个文件
coding是你的私钥,需要好好保管,在下面的配置文件中还需要使用,用以证明你自己的身份,coding.pub是公钥,我们需要上传到github,coding,码云等平台上去。
# 上传公钥
这里只要找到对应上传的地方上传即可,一般平台都有对应的教程,下面我们copy一下coding的操作。
mac 复制文件小技巧
cat coding.pub | pbcopy # 输出到剪切板
pbcopy : 表示复制剪切版 pbpaste :表示粘贴剪切版
# 配置config文件和添加私钥
若是拥有多个账号,需要在 .ssh 下配置多个账号的配置文件 config ,这个配置文件是用来作为路由使用。
我们需要检查是否存在 ~/.ssh/config
文件,不存在的话就创建如下文件。
# ~/.ssh/config
Host github # 别名,使用正则表达式
# connect to github by git@github.com
User git # 用户名
HostName github.com # 服务器地址
IdentityFile ~/.ssh/id_rsa # 私钥地址
Host coding
HostName e.coding.net
User git
IdentityFile ~/.ssh/coding
Host *.works.*
HostName devops.works
IdentityFile ~/.ssh/id_rsa
Host hostmem
HostName 195.85.35.15
IdentityFile ~/.ssh/id_rsa
Host *
User ppsteven # 默认用户
AddKeysToAgent yes # 自动添加到 ssh-agent
IdentityFile ~/.ssh/id_rsa
IdentityFile ~/.ssh/coding
ServerAliveInterval 60
ServerAliveCountMax 3
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
下面解释一下上述配置文件的含义
首先,每一项配置文件的含义都可以通过 man ssh_config
查看,也可以通过
- ssh_config(5) - Linux man page (opens new window) Linux 帮助文件
- ssh_config(5)-OpenBSD (opens new window) MacOS 帮助文件
进行在线查询。
Host 应填写正则表达式,是作为hosts的别称存在
Host: 192.168.1.? # 配置所有 192.168.1.[0-9] 的 HostName: 192.168.1.3 Host targaryen HostName 192.168.1.10 User daenerys Port 7654 IdentityFile ~/.ssh/targaryen.key Host: * User: root # 所用网站都使用的默认账号
1
2
3
4
5
6
7
8
9
10
11HostName
:真实的host地址User
:建立 ssh 连接所用的账号,一般如果是远程登录的话是root
, 用于git
拉取代码的话,一般是git
连接服务器时,会将 User 和 HostName 进行拼接,如
git@github.com
IdentityFile
:私钥存储的地址AddKeysToAgent
: 自动添加私钥到ssh-agent
中去。
# 多账号管理
通过 ~/.ssh/config
文件对多个 git 账号进行管理
,其本质是针对不同的 Host 使用不同的私钥进行验证,所以在使用时需要使用自定义的别名,让 ssh 能根据别名找到对应网站的私钥地址,这种方式和服务器路由匹配的原理类似。
git clone git@e.coding.net:datamate/demo.git # ssh 方式,此时并不会自动识别对应的 ~/.ssh/coding 的私钥地址
git clone coding:datamate/demo.git # 匹配到 coding 规则
git clone git@coding:datamate/demo.git # 匹配到 coding 规则
2
3
通过起别名的方式,根据 ~/.ssh/config
文件,让我们找到不同 Host
的私钥文件位置,那对于不起别名的方式,有没有方法可以做到让系统自动匹配到对应的私钥地址呢? 有,通过 ssh-agent
# ssh-agent是什么
那么 ssh-agent
的作用是什么呢?ssh-agent 是一个用于存储私钥的临时性的 session 服务,当我们使用秘钥登录服务器时,ssh-agent 会启动一个进程在内存里保存这些私钥。之后每次登录时,ssh 客户端都会跟 ssh-agent 请求是否有目标主机的私钥;如果有,ssh 客户端便能直接登录目标主机。
$ ssh-add -D # 清空 ssh-agent 管理的秘钥
All identities removed.
$ ssh root@195.85.35.153 -i myserver # 登录服务器,并使用指定的私钥 myserver
$ ssh-add -l # 此时观察 ssh-agent 存储的秘钥,发现 ssh
2048 SHA256:6PVJJCR1H/3M977orF4EfJgk0d9bZoul538/MtzBXZY ppsteven@ppstevendeMacBook-Pro.local (RSA)
$ ssh root@195.85.35.153 # 此时,就算不指定 myserver 私钥,也能成功登录服务器,原因是ssh已经在ssh-agent中找到了存储的私钥。
2
3
4
5
6
# 基本命令
ssh-agent bash # 启动 ssh-agent 服务
ssh-add -D # 删除之前存储的key
ssh-add -l # 查看存储的key,这里应该是空的
ssh-add ~/.ssh/id_rsa # 添加私钥 id_rsa
ssh-add ~/.ssh/coding # 添加私钥 coding
2
3
4
5
# 如何添加 ssh-agent
手动添加
ssh-add ~/.ssh/coding
每次使用ssh时自动添加
需要配置 ~/.ssh/config
AddKeysToAgent yes # 自动添加私钥到 ssh-agent 中
ssh root@demo.com # 登录成功后会自动添加
ssh -T git@github.com # 测试成功后会自动添加
2
# 测试ssh是否设置成功
# ssh -T 后面的名字是上面的别名
$ ssh -T github
Hi PPsteven! You've successfully authenticated, but GitHub does not provide shell access.
(base)
$ ssh -T coding
Coding 提示: Hello ppsteven, You've connected to Coding.net via SSH. This is a personal key.
ppsteven,你好,你已经通过 SSH 协议认证 Coding.net 服务,这是一个个人公钥
(base)
# 不使用别名时
# 第一次测试的时候,需要指定私钥位置,之后私钥会自动加入 ssh-agent 中
$ ssh -T git@e.coding.net -i ~/.ssh/coding
CODING 提示: Hello ppsteven, You've connected to coding.net via SSH. This is a Personal Key.
ppsteven,你好,你已经通过 SSH 协议认证 coding.net 服务,这是一个个人公钥.
公钥指纹:42:c6:f4:f6:39:1f:d7:84:e0:cb:19:a3:ad:25:62:20
# 私钥先加入 ssh-agent 中,后测试
$ ssh-add ~/.ssh/coding
$ ssh -T git@e.coding.net
CODING 提示: Hello ppsteven, You've connected to coding.net via SSH. This is a Personal Key.
ppsteven,你好,你已经通过 SSH 协议认证 coding.net 服务,这是一个个人公钥.
公钥指纹:42:c6:f4:f6:39:1f:d7:84:e0:cb:19:a3:ad:25:62:20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 自动添加秘钥的几种方法
为什么要自动添加 秘钥
,原因是 ssh-agent
是将所有的秘钥存在内存中,这意味着当电脑重启或初次启动的时候,ssh-agent 中存储的私钥会被清空,我们还是需要做 一次
手动添加私钥的操作。
# 方案一:使用 keychain(Mac-弃用)
keychain 是 Mac 电脑上的钥匙串服务,作用是存储密码、秘钥,证书等信息。Win 和 Linux 也有对应的机制,没有研究。
首先,我们要保证 config
里面有这样两段代码,Mac OS 10.12.2 以上系统需要,不然的话,无法持久化的添加到钥匙串中。
# ~/.ssh/config
+ Host *
+ AddKeysToAgent yes # 把私钥添加到 ssh-agent 中
+ UseKeychain yes # Mac 上秘钥被持久化到"钥匙串"中,代表从钥匙串中使用保存的秘钥
2
3
4
5
此时,当你运行 ssh
后,私钥会自动添加到 ssh-agent和 钥匙串 中,下面在mac中的 钥匙串 找到了添加的私钥。
需要注意的是,Mac上使用 ssh-add -K /path/to/storekey 已经不能直接存储秘钥了,mac 只会存储带 passphrase 的秘钥,对于不带 passphrase 验证的秘钥,Mac的 KeyChain 不会自动保存。可以手动在 Mac 的钥匙串中加入,然后通过 ssh-add -A 添加钥匙串中的秘钥
# 方案二:添加到启动配置中去(通用)
直接将所有的秘钥添加到 ~/.ssh/config
中
Host *
IdentityFile path/to/key1
IdentityFile path/to/key2
2
3
对于 Centos 系统,由于 ssh-agent
不会自动启动,所以需要单独运行 eval "$(ssh-agent -s)"
,参考 Ubuntu、Mac、Centos下ssh-agent 对比 (opens new window)
$ eval "$(ssh-agent -s)"
> Agent pid 59566
$ ssh-add ~/.ssh/id_rsa
2
3
下一个问题,应该把这一段代码添加到哪里。这里的建议是加到或者 .profile
,不建议直接添加到 ~/.bashrc
或 ~/.zshrc
中,因为这样每次打开一个 SHELL
就会开一个 ssh-agent
服务,最终会拖垮服务器。
更多的地方参考我的另一篇教程:linux 环境变量执行顺序
# 参考资料
如何添加多账号
下面是 google 出来的解答,质量很高,建议参考
How to manage multiple GitHub accounts on a single machine with SSH keys (opens new window)
自动添加ssh账号