Git - 更新操作
Git - 更新操作:协作修改
Section titled “Git - 更新操作:协作修改”Tom 克隆并查看 Jerry 的修改
Section titled “Tom 克隆并查看 Jerry 的修改”Tom 执行 git clone 来获取项目,该项目现在包含 Jerry 的 string_utils.c 文件(来自 git_perform_changes.json 场景)。他想查看历史记录,因此执行 git log。
[tom@devbox ~]$ git clone https://git.example.com/our_team/project.gitCloning into 'project'......[tom@devbox ~]$ cd project/[tom@devbox project]$ git loggit log 的输出显示了 Jerry 的提交:
commit abc1234b140dad24b2c35b15cc7e26a6f02d2277 (HEAD -> main, origin/main, origin/HEAD)Author: Jerry Mouse <jerry@example.com>Date: Mon Jan 15 10:00:00 2024 +0000
Implement my_strlen function and main demo
commit 19ae20683fc460db7d127cf201a1429523b0e319Author: Tom Cat <tom@example.com>Date: Sun Jan 14 09:30:00 2024 +0000
Initial commitTom 修改现有函数
Section titled “Tom 修改现有函数”Tom 查看 string_utils.c 并决定改进 my_strlen 函数,明确确保输入指针 s 在函数内部不被修改,尽管 const char *s 已经表明了这一意图。他还注意到原始教程提到了将返回类型更改为 size_t 并使用 const char *s,这是一个很好的实践。假设 Jerry 的初始版本更简单,而 Tom 现在使其更健壮。
Tom 修改 string_utils.c(例如:如果 my_strlen 还没有那样做,则使用 const char *s_ptr = s;,或者添加注释)。假设他为了说明添加了一个小改动:
// 在 string_utils.c 文件中,Tom 添加了一行注释:// 一个计算字符串长度的自定义函数// 通过使用 'p' 确保原始指针未被修改size_t my_strlen(const char *s){ const char *p = s; while (*p) { ++p; } return (p - s);}他使用 git diff 检查差异:
[tom@devbox project]$ git diff输出将显示添加的注释:
diff --git a/string_utils.c b/string_utils.cindex xxxxxxx..yyyyyyy 100644--- a/string_utils.c+++ b/string_utils.c@@ -1,5 +1,6 @@ #include <stdio.h> // 一个计算字符串长度的自定义函数+// 通过使用 'p' 确保原始指针未被修改 size_t my_strlen(const char *s) { const char *p = s;测试后,他提交了他的更改:
[tom@devbox project]$ git status -s M string_utils.c
[tom@devbox project]$ git add string_utils.c
[tom@devbox project]$ git commit -m "为 my_strlen 添加解释性注释"[main def5678] 为 my_strlen 添加解释性注释 1 file changed, 1 insertion(+)
[tom@devbox project]$ git log -1commit def5678e12fff493ba99508c5959e3789fa00000 (HEAD -> main)Author: Tom Cat <tom@example.com>Date: Mon Jan 15 11:00:00 2024 +0000
为 my_strlen 添加解释性注释Tom 将他的更改推送到远程仓库:
[tom@devbox project]$ git push origin main命令输出将显示推送成功:
Enumerating objects: 5, done.Counting objects: 100% (5/5), done....To https://git.example.com/our_team/project.git abc1234..def5678 main -> mainJerry 同时添加一个新函数
Section titled “Jerry 同时添加一个新函数”与此同时,Jerry(在他自己的本地克隆上工作,尚不知道 Tom 最近的推送)决定实现一个字符串复制函数 my_strcpy。他修改了 string_utils.c:
// Jerry 添加到 string_utils.c:char* my_strcpy(char *dest, const char *src){ char *orig_dest = dest; while ((*dest++ = *src++)) ; return orig_dest;}
// 并更新 main 函数进行测试:// ... (之前的 main 代码) char buffer[50]; my_strcpy(buffer, "Hello Git!"); printf("Copied string: %s\n", buffer);// ... (main 函数的其余部分)测试后,Jerry 在本地提交了他的更改:
[jerry@devbox project]$ git status -s M string_utils.c
[jerry@devbox project]$ git add string_utils.c
[jerry@devbox project]$ git commit -m "实现 my_strcpy 函数并添加测试"[main ghi9012] 实现 my_strcpy 函数并添加测试 1 file changed, 10 insertions(+), 2 deletions(-) (示例修改)Jerry 现在想推送他的更改:
[jerry@devbox project]$ git push origin main推送操作失败:
To https://git.example.com/our_team/project.git ! [rejected] main -> main (non-fast-forward)error: failed to push some refs to 'https://git.example.com/our_team/project.git'hint: Updates were rejected because the remote contains work that you dohint: not have locally. This is usually caused by another repository pushinghint: to the same ref. You may want to first integrate the remote changeshint: (e.g., 'git pull ...') before pushing again.hint: See the 'Note about fast-forwards' in 'git push --help' for details.Git 拒绝了 Jerry 的推送,因为远程 main 分支包含 Jerry 本地没有的新提交(来自 Tom)。直接推送会覆盖 Tom 的更改。Git 通过要求 fast-forward 合并来阻止这种情况发生,而这里无法进行 fast-forward 合并。
Jerry 拉取并整合远程修改
Section titled “Jerry 拉取并整合远程修改”Jerry 需要更新他的本地仓库。他使用 git pull。默认情况下,git pull 会拉取更改然后尝试合并它们。一个常见的替代方法是 git pull --rebase,它会将本地提交重新应用到拉取的更改之上,通常会产生更清晰的历史记录。
让我们展示 git pull --rebase 策略:
[jerry@devbox project]$ git pull --rebase origin main输出可能如下所示(假设没有冲突):
From https://git.example.com/our_team/project * branch main -> FETCH_HEADSuccessfully rebased and updated refs/heads/main.如果 Tom 和 Jerry 对 string_utils.c 的更改之间存在冲突(例如,如果他们修改了同一行),Git 将暂停变基并要求 Jerry 解决冲突。假设在这个示例中没有冲突,Jerry 的提交 ghi9012 现在被重新应用在 Tom 的提交 def5678 之后。
Jerry 查看日志。他的本地 main 现在包含了 Tom 的提交,然后是他的(变基后的)提交:
[jerry@devbox project]$ git log --oneline --graph* jkl3456 (HEAD -> main) 实现 my_strcpy 函数并添加测试* def5678 (origin/main, origin/HEAD) 为 my_strlen 添加解释性注释* abc1234 实现 my_strlen 函数及 main 演示* 19ae206 初始提交(注意:jkl3456 是 Jerry 变基后的更改的新提交哈希。)
现在 Jerry 的本地 main 已经是最新的,并包含了远程更改。他可以安全地推送了:
[jerry@devbox project]$ git push origin main这次,推送应该会成功:
Enumerating objects: 7, done.Counting objects: 100% (7/7), done....To https://git.example.com/our_team/project.git def5678..jkl3456 main -> main这种工作流程(拉取/变基,解决冲突(如果有),然后推送)是处理并发更改的常用方法。