Git - Update Operation
Git - Update Operation: Collaborating on Changes
Section titled “Git - Update Operation: Collaborating on Changes”Tom Clones and Reviews Jerry’s Changes
Section titled “Tom Clones and Reviews Jerry’s Changes”Tom performs a git clone to get the project, which now includes Jerry’s string_utils.c file (from the git_perform_changes.json scenario). He wants to see the history, so he executes git log.
[tom@devbox ~]$ git clone https://git.example.com/our_team/project.gitCloning into 'project'......[tom@devbox ~]$ cd project/[tom@devbox project]$ git logThe git log output shows Jerry’s commit:
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 Modifies an Existing Function
Section titled “Tom Modifies an Existing Function”Tom reviews string_utils.c and decides to improve the my_strlen function by explicitly ensuring the input pointer s is not modified within the function, although const char *s already indicates this intent. He also notices the original tutorial mentioned changing the return type to size_t and using const char *s, which is good practice. Let’s assume Jerry’s initial version was simpler, and Tom is now making it more robust.
Tom modifies string_utils.c (example: my_strlen to use const char *s_ptr = s; if it wasn’t already like that, or adds a comment). Let’s say he makes a small change for illustration:
// In string_utils.c, Tom adds a comment:// A custom function to calculate string length// Ensures original pointer is not modified by using 'p'size_t my_strlen(const char *s){ const char *p = s; while (*p) { ++p; } return (p - s);}He checks the differences with git diff:
[tom@devbox project]$ git diffThe output will show the added comment:
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> // A custom function to calculate string length+// Ensures original pointer is not modified by using 'p' size_t my_strlen(const char *s) { const char *p = s;After testing, he commits his change:
[tom@devbox project]$ git status -s M string_utils.c
[tom@devbox project]$ git add string_utils.c
[tom@devbox project]$ git commit -m "Add clarification comment to my_strlen"[main def5678] Add clarification comment to 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
Add clarification comment to my_strlenTom pushes his changes to the remote repository:
[tom@devbox project]$ git push origin mainThe command output will indicate a successful push:
Enumerating objects: 5, done.Counting objects: 100% (5/5), done....To https://git.example.com/our_team/project.git abc1234..def5678 main -> mainJerry Adds a New Function Concurrently
Section titled “Jerry Adds a New Function Concurrently”Meanwhile, Jerry (working on his own local clone, unaware of Tom’s recent push) decides to implement a string copy function, my_strcpy. He modifies string_utils.c:
// Jerry adds to string_utils.c:char* my_strcpy(char *dest, const char *src){ char *orig_dest = dest; while ((*dest++ = *src++)) ; return orig_dest;}
// And updates main to test it:// ... (previous main code) char buffer[50]; my_strcpy(buffer, "Hello Git!"); printf("Copied string: %s\n", buffer);// ... (rest of main)After testing, Jerry commits his changes locally:
[jerry@devbox project]$ git status -s M string_utils.c
[jerry@devbox project]$ git add string_utils.c
[jerry@devbox project]$ git commit -m "Implement my_strcpy function and add test"[main ghi9012] Implement my_strcpy function and add test 1 file changed, 10 insertions(+), 2 deletions(-) (example changes)Jerry now wants to push his changes:
[jerry@devbox project]$ git push origin mainThe push operation fails:
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 rejects Jerry’s push because the remote main branch has new commits (from Tom) that Jerry doesn’t have locally. Pushing directly would overwrite Tom’s changes. Git prevents this by requiring a fast-forward merge, which isn’t possible here.
Jerry Fetches and Integrates Remote Changes
Section titled “Jerry Fetches and Integrates Remote Changes”Jerry needs to update his local repository. He uses git pull. By default, git pull fetches changes and then tries to merge them. A common alternative is git pull --rebase, which re-applies local commits on top of the fetched changes, often resulting in a cleaner history.
Let’s show the git pull --rebase strategy:
[jerry@devbox project]$ git pull --rebase origin mainThe output might look like this (assuming no conflicts):
From https://git.example.com/our_team/project * branch main -> FETCH_HEADSuccessfully rebased and updated refs/heads/main.If there were conflicts between Tom’s and Jerry’s changes to string_utils.c (e.g., if both modified the same lines), Git would pause the rebase and ask Jerry to resolve them. Assuming no conflicts for this example, Jerry’s commit ghi9012 is now re-applied after Tom’s commit def5678.
Jerry checks the log. His local main now includes Tom’s commit, followed by his (rebased) commit:
[jerry@devbox project]$ git log --oneline --graph* jkl3456 (HEAD -> main) Implement my_strcpy function and add test* def5678 (origin/main, origin/HEAD) Add clarification comment to my_strlen* abc1234 Initial commit with my_strlen* 19ae206 Initial commit(Note: jkl3456 is a new commit hash for Jerry’s rebased changes).
Now Jerry’s local main is up-to-date and includes the remote changes. He can safely push:
[jerry@devbox project]$ git push origin mainThis time, the push should succeed:
Enumerating objects: 7, done.Counting objects: 100% (7/7), done....To https://git.example.com/our_team/project.git def5678..jkl3456 main -> mainThis workflow (pull/rebase, resolve conflicts if any, then push) is a common way to handle concurrent changes.