多人项目git协同实践

多人开发的正确工作流程

在dev1分支下我们开发一个新功能,并且在开发的时候分成3个stage提交(方便改bug),但是实际上我们最终只需要提交一个代表该新功能的commit到远端,并合入main分支,应该怎么做呢?

dev

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# dev stage 1
➜ learn-git git:(dev1) git add .
➜ learn-git git:(dev1) ✗ git commit -m "feat: feature 1"
[dev1 53adc27] feat: feature 1
1 file changed, 2 insertions(+), 1 deletion(-)
# dev stage 2
➜ learn-git git:(dev1) git add .
➜ learn-git git:(dev1) ✗ git commit -m "feat: lunch time"
[dev1 3874836] feat: lunch time
1 file changed, 2 insertions(+), 1 deletion(-)
# dev stage 3
➜ learn-git git:(dev1) ✗ git add .
➜ learn-git git:(dev1) ✗ git commit -m "fix: brunch time"
[dev1 94a90c2] fix: brunch time
1 file changed, 1 insertion(+), 1 deletion(-)
# finish dev

local cleanup

查看提交记录,找到对于该功能一共提交了多少次,这里是3次

1
➜  learn-git git:(dev1) git log
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
commit 94a90c28d614326dcbd3a4df2739c5775c60eb78 (HEAD -> dev1)
Author: mmy <15224924822@163.com>
Date: Sat May 18 12:37:41 2024 +0800

fix: brunch time

commit 38748366091e3dbd30a1ce4ab995ece8671720ad
Author: mmy <15224924822@163.com>
Date: Sat May 18 12:37:06 2024 +0800

feat: lunch time

commit 53adc276bae51c26f0bc5c7ca503d017027fe1c4
Author: mmy <15224924822@163.com>
Date: Sat May 18 12:36:42 2024 +0800

feat: feature 1

commit db9cfb80676bb88506a7bfcbb08101ad7df594de (origin/dev1)
Author: mmy <15224924822@163.com>
Date: Thu May 16 14:49:13 2024 +0800

fix: description

需要将这3次提交squash成1次,并且修改提交信息

1
2
➜  learn-git git:(dev1) git rebase -i HEAD~3

Rebasing into Head-3

rebase信息修改前:

1
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
pick 53adc27 feat: feature 1
pick 3874836 feat: lunch time
pick 94a90c2 fix: brunch time

# 变基 db9cfb8..94a90c2 到 db9cfb8(3 个提交)
#
# 命令:
# p, pick <提交> = 使用提交
# r, reword <提交> = 使用提交,但编辑提交说明
# e, edit <提交> = 使用提交,但停止以便修补提交
# s, squash <提交> = 使用提交,但挤压到前一个提交
# f, fixup [-C | -c] <提交> = 类似于 "squash",但只保留前一个提交
# 的提交说明,除非使用了 -C 参数,此情况下则只
# 保留本提交说明。使用 -c 和 -C 类似,但会打开
# 编辑器修改提交说明
# x, exec <命令> = 使用 shell 运行命令(此行剩余部分)
# b, break = 在此处停止(使用 'git rebase --continue' 继续变基)
# d, drop <提交> = 删除提交
# l, label <label> = 为当前 HEAD 打上标记
# t, reset <label> = 重置 HEAD 到该标记
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# . 创建一个合并提交,并使用原始的合并提交说明(如果没有指定
# . 原始提交,使用注释部分的 oneline 作为提交说明)。使用
# . -c <提交> 可以编辑提交说明。
# u, update-ref <引用> = 为引用 <ref> 设置一个占位符,以将该引用更新为此处的新提交。
# 此 <引用> 在变基结束后更新。
-- INSERT --

rebase信息修改后:

1
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
pick 53adc27 feat: feature 1
s 3874836 feat: lunch time
s 94a90c2 fix: brunch time

# 变基 db9cfb8..94a90c2 到 db9cfb8(3 个提交)
#
# 命令:
# p, pick <提交> = 使用提交
# r, reword <提交> = 使用提交,但编辑提交说明
# e, edit <提交> = 使用提交,但停止以便修补提交
# s, squash <提交> = 使用提交,但挤压到前一个提交
# f, fixup [-C | -c] <提交> = 类似于 "squash",但只保留前一个提交
# 的提交说明,除非使用了 -C 参数,此情况下则只
# 保留本提交说明。使用 -c 和 -C 类似,但会打开
# 编辑器修改提交说明
# x, exec <命令> = 使用 shell 运行命令(此行剩余部分)
# b, break = 在此处停止(使用 'git rebase --continue' 继续变基)
# d, drop <提交> = 删除提交
# l, label <label> = 为当前 HEAD 打上标记
# t, reset <label> = 重置 HEAD 到该标记
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# . 创建一个合并提交,并使用原始的合并提交说明(如果没有指定
# . 原始提交,使用注释部分的 oneline 作为提交说明)。使用
# . -c <提交> 可以编辑提交说明。
# u, update-ref <引用> = 为引用 <ref> 设置一个占位符,以将该引用更新为此处的新提交。
# 此 <引用> 在变基结束后更新。
:wq

commit信息修改前:

1
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
# 这是一个 3 个提交的组合。
# 这是第一个提交说明:

feat: feature 1

# 这是提交说明 #2

feat: lunch time

# 这是提交说明 #3

fix: brunch time

# 请为您的变更输入提交说明。以 '#' 开始的行将被忽略,而一个空的提交
# 说明将会终止提交。
#
# 日期: Sat May 18 12:36:42 2024 +0800
#
# 交互式变基操作正在进行中;至 db9cfb8
# 最后完成的命令(3 条命令被执行):
# squash 3874836 feat: lunch time
# squash 94a90c2 fix: brunch time
# 未剩下任何命令。
# 您在执行将分支 'dev1' 变基到 'db9cfb8' 的操作。
#
# 要提交的变更:
"~/develop/codes/learn-git/.git/COMMIT_EDITMSG" 28L, 683B

commit信息修改后:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
feat: feature 1

# 请为您的变更输入提交说明。以 '#' 开始的行将被忽略,而一个空的提交
# 说明将会终止提交。
#
# 日期: Sat May 18 12:36:42 2024 +0800
#
# 交互式变基操作正在进行中;至 db9cfb8
# 最后完成的命令(3 条命令被执行):
# squash 3874836 feat: lunch time
# squash 94a90c2 fix: brunch time
# 未剩下任何命令。
# 您在执行将分支 'dev1' 变基到 'db9cfb8' 的操作。
#
# 要提交的变更:
# 修改: README.md
:wq

再次git log发现就只剩一条

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
commit 6188a860fc471e7f364a0d5348c78293df547a2a (HEAD -> dev1)
Author: mmy <15224924822@163.com>
Date: Sat May 18 12:36:42 2024 +0800

feat: feature 1

commit db9cfb80676bb88506a7bfcbb08101ad7df594de (origin/dev1)
Author: mmy <15224924822@163.com>
Date: Thu May 16 14:49:13 2024 +0800

fix: description

commit 38f1ba6768ee74dcdc6dbaf04def41844474deda
Author: mmy <15224924822@163.com>
Date: Thu May 16 14:46:29 2024 +0800

first commit
(END)

rebase to main

我们目前已经处理好本次提交的相关信息了,能不能直接推远端呢?

答案是不能

因为在我们开发的时候也有其他同学在开发新的功能,如果别人比我们开发得早,那我们这个feature肯定有冲突

  • 如果从本地特性分支直接推到远程主分支,控制台会报错

    1
    2
    3
    4
    5
    6
    7
    8
    9
    ➜  learn-git git:(dev1) git push origin main
    Enter passphrase for key '/Users/mmy/.ssh/id_rsa':
    To github.com:hugtyftg/learn-git.git
    ! [rejected] main -> main (fetch first)
    错误:无法推送一些引用到 'github.com:hugtyftg/learn-git.git'
    提示:更新被拒绝,因为远程仓库包含您本地尚不存在的提交。这通常是因为另外
    提示:一个仓库已向该引用进行了推送。如果您希望先与远程变更合并,请在推送
    提示:前执行 'git pull'
    提示:详见 'git push --help' 中的 'Note about fast-forwards' 小节。
  • 如果本地特性分支推到远程特性分支,然后给远程主分支提mr,pipeline automatic merge也会提示我们存在conflicts

    未rebase提mr

无论如何都要解决冲突!

要想解决冲突,一定要在特性分支上rebase保证线性提交历史

Merge VS Rebase

本地main分支同步远程repo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
➜  learn-git git:(dev1) git checkout main
切换到分支 'main'
您的分支与上游分支 'origin/main' 一致。
➜ learn-git git:(main) git pull origin main
Enter passphrase for key '/Users/mmy/.ssh/id_rsa':
remote: Enumerating objects: 1, done.
remote: Counting objects: 100% (1/1), done.
remote: Total 1 (delta 0), reused 0 (delta 0), pack-reused 0
展开对象中: 100% (1/1), 879 字节 | 879.00 KiB/s, 完成.
来自 github.com:hugtyftg/learn-git
* branch main -> FETCH_HEAD
839752c..707c9cf main -> origin/main
更新 839752c..707c9cf
Fast-forward
07 Rebasing into Head-3.svg | 161 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
09 Rebase vs Merge.svg | 1 +
README.md | 47 ++++++++++++++++++++++++++--
3 files changed, 206 insertions(+), 3 deletions(-)
create mode 100644 07 Rebasing into Head-3.svg
create mode 100644 09 Rebase vs Merge.svg

特性分支rebase main

1
2
3
4
5
6
7
8
9
10
11
12
➜  learn-git git:(main) git checkout dev1
切换到分支 'dev1'
您的分支与上游分支 'origin/dev1' 一致。
➜ learn-git git:(dev1) git rebase main
自动合并 README.md
冲突(内容):合并冲突于 README.md
错误:不能应用 6188a86... feat: feature 1
提示:Resolve all conflicts manually, mark them as resolved with
提示:"git add/rm <conflicted_files>", then run "git rebase --continue".
提示:You can instead skip this commit: run "git rebase --skip".
提示:To abort and get back to the state before "git rebase", run "git rebase --abort".
不能应用 6188a86... feat: feature 1

有冲突的话需要解决冲突

1
2
3
4
5
6
7
8
9
10
11
12
13
feat: feature 1

# 请为您的变更输入提交说明。以 '#' 开始的行将被忽略,而一个空的提交
# 说明将会终止提交。
#
# 交互式变基操作正在进行中;至 707c9cf
# 最后完成的命令(1 条命令被执行):
# pick 6188a86 feat: feature 1
# 未剩下任何命令。
# 您在执行将分支 'dev1' 变基到 '707c9cf' 的操作。
#
# 要提交的变更:
# 修改: README.md

冲突解决之后保存更改、继续rebase

1
2
3
4
5
➜  learn-git git:(707c9cf) ✗ git add .
➜ learn-git git:(707c9cf) ✗ git rebase --continue
[分离头指针 5bca03c] feat: feature 1
1 file changed, 4 insertions(+), 1 deletion(-)
成功变基并更新 refs/heads/dev1

推送合入!

由于刚才已经给远程仓库push过一次了,所以这里需要强制推送覆盖远程dev1分支(万分注意-f

1
➜  learn-git git:(dev1) git push origin dev1 -f

推完之后可以发现pipeline又跑了一遍,并且这次没有conflicts了,可以准许合入。不过实际工作项目中还会有+1给你cr,ap之后才会合入……(ps: 实习阶段cr问题多千万要稳住心态,我曾经一个feat cr了6次才合入😭)

rebase后提mr


多人项目git协同实践
https://hugtyftg.github.io/2024/05/18/多人项目git协同实践/
作者
mmy@hugtyftg
发布于
2024年5月18日
许可协议