Git

Git 配置多用户 & reset/revert 简介 & 合并 Commit

一个电脑配置两个github用户 & reset和revert简介 & 以及如何合并Commit

Posted by BlackDn on October 4, 2022

“纵使黑夜孤寂,白昼如焚。”

Git 配置多用户 & reset/revert 简介 & 合并 Commit

前言

国庆快乐,虽然要过半了
对于 git 操作,之前写过一篇文章:Git 基本操作及连接 Github
这篇文章属于是一个番外吧
因为遇到了这个问题还卡了我好一会
所以还是记录一下嗷

一个设备配置多个 Github 用户

其实一般情况下也不会有这个需求,但是因为公司的工作账号和个人账号是两个 Github 账号,然后为了方便摸鱼在工作之余折腾一些自己的东西(比如我的 Github Page),所以还是想在一台机子上配置两个账户的。

生成 ssh 密钥

很多配置多个账户的文章都提到要大家生成两个 ssh 密钥,其实这是非必需的。重要的是在本地配置一个config配置文件(所以其实可以在配置文件中声明用同一个 ssh 密钥)但是为了方便起见,这里还是配置了连两个密钥。
配置密钥的具体步骤可以参见:配置 SSH 密钥

编辑配置文件

配置文件config是实现多用户的精髓所在,我们需要在~/.ssh/目录下新建一个config文件(Windows 应该在/c/Users/you/.ssh/的目录下),文件包含内容如下:

# user1
Host user1.github.com    #纯纯自定义的内容
HostName github.com    #这个不用变,除非是gitLab等其他平台
PreferredAuthentications publickey    #认证方式,我们用ssh公钥
IdentityFile ~/.ssh/id_rsa    #这个对应账号下加入的ssh认证文件
User user1    # 自定义的名字

# user2
Host user2.github.com
HostName github.com
PreferredAuthentications publickey
IdentityFile ~/.ssh/id_ed25519
User user2

其实需要变的只有IdentityFileHost的内容。在user1的 github 账号中我添加的 ssh 是id_rsa.pubuser2添加的是id_ed25519.pub,所以在IdentityFile中就要进行一个区分。

尝试连接

然后我们可以测试一下和 github 的连接。以往我们用的命令是:ssh -T git@github.com,但是现在为了告诉 github 我们连接的是哪个账户,需要对@后面的内容进行一个修改,改成我们的自己配置的Host

~ ssh -T git@user1.github.com
Hi user1! You've successfully authenticated, but...

~ ssh -T git@user2.github.com
Hi user2! You've successfully authenticated, but...

这样一来,基本就能在一台机子上成功区分两个用户了。
不过还有最后一个坑点

连接仓库

最后一个坑点在于,我们克隆项目代码的时候,不能直接输入复制来的 ssh 命令,同样要把@后面的内容进行一个改,以OkHttp为例:

#普通clone OkHttp:
git@github.com:square/okhttp.git
# 以user1去clone OkHttp:
git@user1.github.com:square/okhttp.git
# 以user2去clone OkHttp:
git@user2.github.com:square/okhttp.git

因为有些仓库设置了权限(private 啥的),或者只有 Organization 里的账户允许提交仓库啥的(比如我的工作账号),所以还是要必要在一开始的时候明确使用的是哪个账号 clone 的。

此外,对于那些已经clone到本地的项目,再进行远程操作的时候可能会出现一些问题(比如push的时候会有权限问题啥的):

ERROR: Permission to user2/project.git denied to user1.
fatal: Could not read from remote repository.

这是由于 git 没有正确识别到当前仓库的用户所导致的,上面的情况就说明当前是 user1,所以没有权限访问 user2 的仓库。有两个方法可以解决这个问题:

第一个方法是重新关联一次远程仓库。先解除关联,再重新关联。不过要注意的是重新关联的时候,要用我们config里的Host内容代替原来的 ssh 命令:

# 删除关联的远程仓库
git remote remove origin
# 添加关联远程仓库
git remote add origin git@user2.github.com:user2/project.git

第二个方法就是手动修改本地仓库目录下.git/config中的url信息:

[remote "origin"]
    url = git@user2.github.com:user2/project.git

Git 用户配置

之前我们只有一个账号的时候,我们采用的是 git 的全局配置来设定我们当前的nameemail

$ git config --global --list
user.name=BlackDn
user.email=xxx@qq.com
core.safecrlf=warn
core.autocrlf=input

而如今在多用户的情况下,虽然说可以不进行额外的配置,但这样会让我在这个设备上所有操作的nameemail都是这个全局设置的内容。我的同事小伙伴肯定会为这个叫BlackDn的人感到疑惑。
因此,如果有需要的话,我们可以为每个仓库设置自己的nameemail
首先来删除全局的配置:

git config --global --unset 'user.name'
git config --global --unset 'user.email'

然后进入到某个仓库的目录,为其单独配置:

git config user.name `user1`
git config user.email `xxx@xxx.com`

个人多用户配置分享

上述方法是通过全局的方式区分两个 Github 用户,好处是非常直观,坏处就是每次克隆仓库的时候都要手动修改,很麻烦。
对于我个人来说,因为是工作电脑,大多数时候需要用公司的 Github 账号,因为我想要不修改仓库地址,默认克隆的时候就是通过公司账号进行的,~/.ssh/config配置如下:

# blackdn(xxx@qq.com)
Host user1.github.com
HostName github.com
PreferredAuthentications publickey
IdentityFile ~/.ssh/id_rsa
User blackdn

# work-account(xxx@xx.com)
Host github.com
HostName github.com
PreferredAuthentications publickey
IdentityFile ~/.ssh/id_ed25519
User yang

可以看到,工作账号的Host没有任何修改,直接是github.com;而个人账号用的比较少,所以给他加一个前缀,需要用个人账号克隆仓库的时候再改一下克隆地址。

此外,如果你想要保险起见,可以在克隆下来的仓库中单独声明对这个仓库操作的账户,避免一些权限问题(比如没权限push啥的)
单独声明的方式和全局类似,在仓库的隐藏目录.git中配置一个config文件,在其中

git reset 和 git revert

git reset

git reset侧重于版本切换,我们每次commit,在 git 看来都是一个新版本的发布,并会自动给我们生成一个版本号,可以用git loggit reflog查看(具体可见:Git 基本操作及连接 Github)。
我们可以用git reset 版本号的形式回到对应版本,也可以用git reset HEAD^回到上一个版本(HEAD^^就是回到上上个版本)

在使用git reset的时候,默认参数是--mixed,也就是说git reset --mixed HEAD^git reset HEAD^效果是一样的,它表示:撤销commit,并且撤销git add,但是我们所做的代码改动是会保留的。
此外,我们常用到--soft参数和--hard参数。
--soft表示:撤销commit,不撤销git add ,同时保留代码改动,相当于就是把commit的东西变成add状态;
--hard表示:撤销commit,撤销git add,并且删除代码改动,相当于直接回到了上一次commit完的状态,中间的修改都没啦。

git revert

git revert本质还是一次commit提交,只不过它的提交内容和上次的commit内容相反,即“否定上一个提交的提交”。
上一个 commit 加了一行,那么 revert 就删一行;上一个 commit 加了个文件,那么 revert 就删这个文件,等等。

git reset类似,git revert 版本号git revert HEAD^等方式都是可以的。

两者差别

git reset主要用于本地的回退操作,而git revert的设计初衷是为了更安全地撤销已经push的提交。毕竟 git 作为版本管理工具,很多时候是很多个人进行团队开发的。revert会在 history 中保留被撤销的commit,更利于团队交流协作。(当然如果不想让别人知道自己的错误代码还是可以用reset的嘿嘿)

对 Commit 进行操作

合并多个 Commit

很多时候我们想对本地的一些 commit 进行一些操作,比如要合并多个 commit,或删除其中的几个 commit 之类的。这些操作都可以通过git rebase -i实现

比如我们想合并之前的三个 commit,可以输入命令git rebase -i HEAD~3(前两个就是HEAD~2),然后会进入 vim 编辑文本,大致如下:

└─(14:55:20 on master)──> git rebase -i HEAD~3
pick f9bfad4 [blackdn] refactor: adjust css
pick f824f9b [blackdn] refactor: adjust css
pick 896d406 [blackdn] refactor: adjust css for no padding

# Rebase 424a879..896d406 onto 424a879 (3 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup [-C | -c] <commit> = like "squash" but keep only the previous
#                    commit's log message, unless -C is used, in which case
#                    keep only this commit's message; -c is same as -C but
#                    opens the editor
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# .       create a merge commit using the original merge commit's
# .       message (or the oneline, if no original merge commit was
# .       specified); use -c <commit> to reword the commit message
#
# These lines can be re-ordered; they are executed from top to bottom.
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.

这里要注意一点,就是这里展现的 commit 顺序是倒叙的,也就是说896d406是最新的一个 commit,上一个是f824f9b,上上个是f9bfad4
然后我们要做的就是通过修改每条 commit 前的词,不同的词所代表的含义每次都会以注释的形式出现。现在这三个前面是pick,表示正常的 commit,这三个 commit 会在记录中正常显示。

上面我提交了三个 commit,都是对 css 的修改。我想要合并他们成为一个,就要把前面的pick改成squashs。这里要注意一点,由于 squash 是向上合并(meld into previous commit),因此我们需要将最新的两个(896d406f824f9b)改成squash,表示合并到他们之前的一个 commit 中(f9bfad4):

pick f9bfad4 [blackdn] refactor: adjust css
squash f824f9b [blackdn] refactor: adjust css
squash 896d406 [blackdn] refactor: adjust css for no padding

保存后会进入另外一个页面,选择自己喜欢的 Commit Message:

└─(15:20:44 on f9bfad4)──> git rebase --continue
# This is the commit message #1:
[blackdn] refactor: adjust css
# This is the commit message #2:
[blackdn] refactor: adjust css
# This is the commit message #3:
[blackdn] refactor: adjust css for no padding

这里会列出所有涉及 commit 的 message,下面还有一大段注释提示操作,但是我没贴出来,简单来说就是你选一个喜欢的 message 修改(不改也行),然后把其他的 message 给注释了,保存退出,最后就留下的 message 就作为合并后 commit 的 message。
我这里选择第三个 message 作为最后的 message:

└─(15:20:44 on f9bfad4)──> git rebase --continue
# This is the commit message #1:
# [blackdn] refactor: adjust css
# This is the commit message #2:
# [blackdn] refactor: adjust css
# This is the commit message #3:
[blackdn] refactor: adjust css for no padding

同理,如果想要删除某个 commit 就可以把前面改成drop,确认后那个 commit 就会从纪录里删除。

更改 Commit Message

有些时候我们发现 Commit Message 写错了或者不够确切,想要进行修改。一般情况下我们会想到用reset回退并重新提交,但是这样非常麻烦,还容易产生新的错误或冲突啥的。
这时候可以用git commit --amend,执行该命令后会进入 vim 编辑器,修改第一行的注释后保存即可。

参考

  1. 如何在一台电脑上设置多个 github 账号

  2. 记一次 git 多用户提交失败的折腾经验

  3. Git 基本操作及连接 Github

  4. git commit 之后,想撤销 commit

  5. 面试官:说说你对 Git Reset 和 Git Revert 的理解?区别?

  6. git 合并多个 commit