git-use

"git的常用使用技巧"

Posted by author.zero on 2023-04-08

“Never complain, never explain. Resist the temptation to defend yourself or make excuses. ”

git的常用使用技巧

includeIf

在一些时候,我们希望 git 有不同的配置。比如在公司的项目中你使用的 user.name王思葱user.emailwsc@abc.com。在开源项目或者自己的项目中使用的user.name沸羊羊user.emailfyy@def.com

可能大家一般配置的时候是这样
git config user.name "王思葱"
或者这样
git config --global user.name "沸羊羊"

在这里首先说一下git config支持系统层级 --system、 用户层级 --global 与仓库层级(无选项)的配置git config

但是, 手动地通过 git config 指定未免过于繁琐。 本文介绍了一种通过修改 git 的全局配置文件 .gitconfig,使用 [includeIf]某个文件夹下的所有 git 项目指定 git 配置的方法。

从 git 2.13.0 开始,git 配置文件开始支持 Conditional Includes 的配置。通过设置 includeIf.<condition>.path,可以向命中 condition 的 git 仓库引入 path 指向的一个 git 配置文件中配置。

[includeIf] 的语法如下,<keyword> 为关键词,<data> 是与关键词关联的数据, 具体意义由关键词决定。

1
2
[includeIf "<keyword>:<data>"]
path = path/to/gitconfig

其中支持的 keyword 有:

  • gitdir: 其中 <data> 是一个 glob pattern 如果代码仓库的.git目录匹配 <data> 指定的 glob pattern,那么条件命中;
  • gitdir/igitdir的大小写不敏感版本。
  • onbranch:其中 <data> 是匹配分支名的一个glob pattern。 假如代码仓库中分支名匹配 <data>,那么条件命中。

就我们的需求,使用 gitdir 完全可以。

例子

假设在家用工作电脑上,我们默认开发的是个人项目。有时为了应对紧急需求, 会将公司项目 clone 到电脑中,统一放置放到 ~/corp-projects/ 目录下面。 个人项目与公司项目的差异点在:第一、使用的邮箱名不同, 个人项目会使用个人邮箱,公司项目使用公司邮箱;第二, 公司项目可能需要 VPN 接入才能够存取代码库。 我们首选使用,用户层级的 git 配置文件。

1
vim ~/.gitconfig

在最后添加一个 conditional include:

1
2
3
# ~/corp-projects/ 下面的所有仓库引入 `~/crop-projects/.gitconfig` 中的配置
[includeIf "gitdir:~/corp-projects/"]
path = ~/corp-projects/.gitconfig

最后创建公司项目统一的配置文件:

1
vim ~/corp-projects/.gitconfig
1
2
3
4
5
6
7
[user]
name = <Your Name>
email = <Your Email>

[http]
# 代理地址,如果公司项目需要代理才能够存取,填写此项;如果不需要,则不用这一行
proxy = <Proxy URL>

cherry-pick

chary-pick直译为樱桃🍒采摘,所以其实它的作用和采摘有很大的关系。当你需要合并A分支中的某些commit而不是直接合并A分支,这时候你只需要采摘A分支中的某几个commit合并进去

基本用法

git cherry-pick命令的作用,就是将指定的提交(commit)应用于其他分支。

1
2

$ git cherry-pick <commitHash>

上面命令就会将指定的提交commitHash,应用于当前分支。这会在当前分支产生一个新的提交,当然它们的哈希值会不一样。

举例来说,代码仓库有masterfeature两个分支。

1
2
3
4

a - b - c - d Master
\
e - f - g Feature

现在将提交f应用到master分支。

1
2
3
4
5
6

# 切换到 master 分支
$ git checkout master

# Cherry pick 操作
$ git cherry-pick f

上面的操作完成以后,代码库就变成了下面的样子。

1
2
3
4

a - b - c - d - f Master
\
e - f - g Feature

从上面可以看到,master分支的末尾增加了一个提交f

git cherry-pick命令的参数,不一定是提交的哈希值,分支名也是可以的,表示转移该分支的最新提交。

1
2

$ git cherry-pick feature

上面代码表示将feature分支的最近一次提交,转移到当前分支。

转移多个提交

Cherry pick 支持一次转移多个提交。

1
2

$ git cherry-pick <HashA> <HashB>

上面的命令将 A 和 B 两个提交应用到当前分支。这会在当前分支生成两个对应的新提交。

如果想要转移一系列的连续提交,可以使用下面的简便语法。

1
2

$ git cherry-pick A..B

上面的命令可以转移从 A 到 B 的所有提交。它们必须按照正确的顺序放置:提交 A 必须早于提交 B,否则命令将失败,但不会报错。

注意,使用上面的命令,提交 A 将不会包含在 Cherry pick 中。如果要包含提交 A,可以使用下面的语法。

1
2

$ git cherry-pick A^..B

git-stash

发现有一个类是多余的,想删掉它又担心以后需要查看它的代码,想保存它但又不想增加一个脏的提交。这时就可以考虑git stash。
使用git的时候,我们往往使用分支(branch)解决任务切换问题,例如,我们往往会建一个自己的分支去修改和调试代码, 如果别人或者自己发现原有的分支上有个不得不修改的bug,我们往往会把完成一半的代码commit提交到本地仓库,然后切换分支去修改bug,改好之后再切换回来。这样的话往往log上会有大量不必要的记录。其实如果我们不想提交完成一半或者不完善的代码,但是却不得不去修改一个紧急Bug,那么使用git stash就可以将你当前未提交到本地(和服务器)的代码推入到Git的栈中,这时候你的工作区间和上一次提交的内容是完全一样的,所以你可以放心的修Bug,等到修完Bug,提交到服务器上后,再使用git stash apply将以前一半的工作应用回来。

stash当前修改

1
2
3
$ git stash
Saved working directory and index state WIP on master: 5002d47 our new homepage
HEAD is now at 5002d47 our new homepage

实际应用中推荐给每个stash加一个message,用于记录版本

1
2
3
4
5
$ git stash save "test-cmd-stash"
Saved working directory and index state On autoswitch: test-cmd-stash
HEAD 现在位于 296e8d4 remove unnecessary postion reset in onResume function
$ git stash list
stash@{0}: On autoswitch: test-cmd-stash

重新应用缓存的stash

1、将缓存堆栈中的第一个stash删除,并将对应修改应用到当前的工作目录下
1
$ git stash pop
2、可以使用git stash apply命令,将缓存堆栈中的stash多次应用到工作目录中,但并不删除stash拷贝
1
$ git stash apply

可以使用名字指定使用哪个stash,默认使用最近的stash(即stash@{0})

3、 查看现有stash
1
2
3
4
$ git stash list
stash@{0}: WIP on master: 049d078 added the index file
stash@{1}: WIP on master: c264051 Revert "added file_size"
stash@{2}: WIP on master: 21d80a5 added number to log
4、 移除stash
1
2
3
4
5
6
$ git stash list
stash@{0}: WIP on master: 049d078 added the index file
stash@{1}: WIP on master: c264051 Revert "added file_size"
stash@{2}: WIP on master: 21d80a5 added number to log
$ git stash drop stash@{0}
Dropped stash@{0} (364e91f3f268f0900bc3ee613f9f733e82aaed43)
5、 查看指定stash的diff

可以使用git stash show命令,后面可以跟着stash名字。示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$ git stash show
index.html | 1 +
style.css | 3 +++
2 files changed, 4 insertions(+)
在该命令后面添加-p或--patch可以查看特定stash的全部diff,如下:

$ git stash show -p
diff --git a/style.css b/style.css
new file mode 100644
index 0000000..d92368b
--- /dev/null
+++ b/style.css
@@ -0,0 +1,3 @@
+* {
+ text-decoration: blink;
+}
diff --git a/index.html b/index.html
index 9daeafb..ebdcbd2 100644
--- a/index.html
+++ b/index.html
@@ -1 +1,2 @@
+<link rel="stylesheet" href="style.css"/>
6、 从stash创建分支
1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ git stash branch testchanges
Switched to a new branch "testchanges"
# On branch testchanges
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# modified: index.html
#
# Changes not staged for commit:
# (use "git add <file>..." to update what will be committed)
#
# modified: lib/simplegit.rb
#
Dropped refs/stash@{0} (f0dfc4d5dc332d1cee34a634182e168c4efc3359)

该命令会用stash中的修改创建一个新的分支,创建成功后会删除此stash

git rebase

在合并代码的时候很多时候我们都是使用 git merge , 但是merge在 合并代码的时候会产生一个新的commit节点,表示这里合并过代码。所以 在一些情况下如果你只是想合并上游的分支代码,而不想产生一个毫无意义的commit节点。这时候就可以采用git rebase 让你提取上游分支的改动来合并到你的分支中,而不会产生额外的节点

如果上游分支已经包含您所做的更改(例如,因为您邮寄了上游应用的补丁),则该提交将被跳过并发出警告(如果使用合并后端)。例如,在以下历史记录上运行 git rebase master (其中 A'A 引入相同的更改集,但具有不同的提交者信息):

1
2
3
      A---B---C topic
/
D---E---A'---F master

将导致:

1
2
3
               B'---C' topic
/
D---E---A'---F master

以下是如何将基于一个分支的主题分支移植到另一个分支,以假装您使用 rebase --onto 从后一个分支分叉主题分支。
首先,我们假设您的主题基于下一个分支。例如,主题中开发的功能取决于下一个中找到的某些功能。

1
2
3
4
5
6
    o---o---o---o---o  master
\
o---o---o---o---o next
\
o---o---o topic
我们想让主题从分支主分支中分叉出来;例如,因为 topic 所依赖的功能被合并到更稳定的 master 分支中。我们希望我们的树看起来像这样:
1
2
3
4
5
6
    o---o---o---o---o  master
| \
| o'--o'--o' topic
\
o---o---o---o---o next
我们可以使用以下命令来获取它:
1
git rebase --onto master next topic

–onto 选项的另一个示例是对分支的一部分进行变基。如果我们有以下情况:

1
2
3
4
5
                        H---I---J topicB
/
E---F---G topicA
/
A---B---C---D master

然后命令

1
git rebase --onto master topicA topicB

会导致:

1
2
3
4
5
             H'--I'--J'  topicB
/
| E---F---G topicA
|/
A---B---C---D master

当 topicB 不依赖于 topicA 时,这很有用。

一系列提交也可以通过变基来删除。如果我们有以下情况:

1
E---F---G---H---I---J  topicA

然后命令

1
git rebase --onto topicA~5 topicA~3 topicA

将导致删除提交 F 和 G:

1
E---H'---I'---J'  topicA

如果 F 和 G 在某些方面有缺陷,或者不应该成为 topicA 的一部分,那么这很有用。请注意, --onto 的参数和 <upstream> 参数可以是任何有效的提交。

如果发生冲突, git rebase 将在第一个有问题的提交处停止,并在树中留下冲突标记。您可以使用 git diff 定位标记 (<<<<<<) 并进行编辑以解决冲突。对于您编辑的每个文件,您需要告诉 Git 冲突已解决,通常可以使用

1
git add <filename>

手动解决冲突并使用所需的解决方案更新索引后,您可以继续执行变基过程

1
git rebase --continue

或者,您可以使用以下命令撤消 git rebase

1
git rebase --abort

参考链接