#90DaysOfDevOps - Day 11: Advance Git & GitHub for DevOps Engineers: Part-2
Introduction
Welcome to Day 11 of the 90DaysDevOps Challenge! In Part 2, we're delving into advanced Git techniques: git stash, git cherrypick, and conflict resolution. No long lectures here – just hands-on exercises. Picture effortlessly moving changes from dev to production using stash and cherrypick, and mastering conflict resolution. Let's own Day 11!
git stash
Imagine you're working on a project using Git, which keeps track of your code changes. Sometimes, you need to temporarily put aside your current changes and work on something else.
Think of Git stash like a magic box. When you use it, your ongoing changes are put into this box so you can switch to a different task. But the cool thing is, even after you take those changes out of the box and use them, the changes are still kept safely inside.
This is helpful when you're fixing a problem. You can put your changes in the box, then test if your fix works on different parts of the project without messing up everything. This is better than mixing all your changes together, which can be confusing.
So, when you're ready to share your fix, you know it works because you tried it in different places. And when you ask others to add your fix to their work, you're sure you're only giving them what they need, not extra stuff.
Commands
git stash
stash the changes that have been added to your index (staged changes) and changes made to files that are currently tracked by Git (unstaged changes).
git stash list
view the stash list
git stash pop
removes the most recent changes from the stash and reapplies them in the working copy
git stash apply
reapplies the changes in the working copy without removing the changes from the stash
git stash clear
deletes all stashes
git cherrypick
At times, there's a need to move a specific update from one branch to another without including all the other changes from the starting branch. To achieve this, Git offers a command called cherry-pick.
In Git, cherry-pick involves selecting a single commit from one branch and adding it to a different one. This stands in contrast to methods like merge and rebase, which usually involve adding multiple commits to another branch.
Command
git cherry-pick <commit_hash>
pick the commit associated with the specified commit hash to the current branch.
Git conflicts and resolution
Merging Changes: In Git, merging means adding changes from one workspace (branch) to another. Think of branches like separate workspaces.
Using Git makes merging easy. Usually, it figures out how to mix changes. But sometimes, when changes clash (like two people editing the same part of a file), you need to help Git decide. For example, if two people change the same lines in a file, Git can't choose. It's like two people changing a sentence differently. Git marks this as a "conflict," and you fix it before moving on.
Handling Conflicts: When there's a problem, find out what's up. Did others mess with your work? Delete it? Make files with the same name?
Git says there are issues by showing conflicts. You see this with "git status." It's like a flag to sort things out.
Fixing Conflicts: When there's a conflict, open the file in your code editor. Git guides you with "<<<<<<< HEAD" and ">>>>>>> [other/branch/name]" around the problem. After "<<<<<<< HEAD" is your version. The second marker shows where changes came from. "=====" splits the conflicts. It's like signs for the problem.
Now, fix these lines. The file should look how you want. Sometimes, talk with your teammate who made the conflicting changes to pick the right code. It might be yours, theirs, or a mix.
Task 1 - git stash
Create a new branch and make some changes to it.
Use git stash to save the changes without committing them.
Switch to a different branch, make some changes, and commit them.
Use git stash pop to bring the changes back and apply them on top of the new commits.
We will create "feature-1" branch and add a single commit. Then we make two changes.
NOTE: git stash doesn't work when new files are created. It only works on modified files.
#switch to main branch ubuntu@~/Devops$: git checkout main Already on 'main' Your branch is up to date with 'origin/main'. #create and switch to new branch feature-1 ubuntu@~/Devops$: git checkout -b feature-1 Switched to a new branch 'feature-1' ubuntu@~/Devops$: mkdir feature-1 ubuntu@~/Devops$: cd feature-1/ ubuntu@~/Devops/feature-1$: echo "First functionality of feature 1" > feature1.txt #stage and commit the changes ubuntu@~/Devops$: git add . ubuntu@~/Devops$: git commit -m "Added first functionality to feature 1" [feature-1 485b829] Added first functionality to feature 1 1 file changed, 1 insertion(+) create mode 100644 feature-1/feature1.txt #add few more changes ubuntu@~/Devops/feature-1$: echo "Second functionality of feature 1" >> feature1.txt ubuntu@~/Devops/feature-1$: echo "Final functionality of feature 1" >> feature1.txt ubuntu@~/Devops/feature-1$: cat feature1.txt First functionality of feature 1 Second functionality of feature 1 Final functionality of feature 1
Now that we have modified the "feature-1.txt" file, we will use
git stash
and view the results.#check the status of repository ubuntu@~/Devops/feature-1$: git status On branch feature-1 Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory) modified: feature1.txt no changes added to commit (use "git add" and/or "git commit -a") #use git stash ubuntu@~/Devops/feature-1$: git stash Saved working directory and index state WIP on feature-1: 485b829 Added first functionality to feature 1 #view the status of repository after git stash #the changes have been added to stash ubuntu@~/Devops/feature-1$: git status On branch feature-1 nothing to commit, working tree clean #view the changes present in stash ubuntu@~/Devops/feature-1$: git stash show feature-1/feature1.txt | 2 ++ 1 file changed, 2 insertions(+)
We switch to the "dev" branch and add a change.
ubuntu@~/Devops/feature-1$: cd .. ubuntu@~/Devops$: git checkout dev Switched to branch 'dev' ubuntu@~/Devops$: git status On branch dev nothing to commit, working tree clean ubuntu@~/Devops$: echo "This is bug fix" >> git/version02.txt ubuntu@~/Devops$: cat git/version02.txt This is feature 2 implementation This is feature 3 implementation Bug found in feature 2. Fixing the bug This is bug fix ubuntu@~/Devops$: git add . && git commit -m "Fixed a bug" [dev 119ada7] Fixed a bug 1 file changed, 1 insertion(+) #use git stash pop. #throws an error stating that there is conflict ubuntu@~/Devops$: git stash pop CONFLICT (modify/delete): feature-1/feature1.txt deleted in Updated upstream and modified in Stashed changes. Version Stashed changes of feature-1/feature1.txt left in tree. The stash entry is kept in case you need it again. #resolve the conflict by creating feature-1/feature1.txt ubuntu@~/Devops$: touch feature-1/feature1.txt #use git stash pop again ubuntu@~/Devops$: git stash pop On branch dev Changes to be committed: (use "git restore --staged <file>..." to unstage) new file: feature-1/feature1.txt Dropped refs/stash@{0} (f89cfb8ab50bff7f79a06007fcf1b48aabd57609) #check the changes from stash ubuntu@~/Devops$: cat feature-1/feature1.txt First functionality of feature 1 Second functionality of feature 1 Final functionality of feature 1 #verify whether there are any more changes in stash ubuntu@~/Devops$: git stash show No stash entries found.
After we resolved the conflict, we were able to remove and use the changes that were stashed. After the
git stash pop
, the changes are staged to the "dev" branch.
Task 2 - git rebase
In version01.txt of the development branch add the below lines after “This is the bug fix in development branch” that you added in Day10 and reverted to this commit.
Line2>> After bug fixing, this is the new feature with minor alterations”
Commit this with the message “ Added feature2.1 in development branch”
Line3>> This is the advancement of the previous feature
Commit this with the message “ Added feature2.2 in development branch”
Line4>> Feature 2 is completed and ready for release
Commit this with the message “ Feature2 completed”
All these commits messages should be reflected in the Production branch too which will come out from the main branch (Hint: try rebase)
#switch to dev branch ubuntu@~/Devops$: git checkout dev Switched to branch 'dev' #adding first change ubuntu@~/Devops$: echo "This is the bug fix in development branch" > version01.txt #stage and commit the change ubuntu@~/Devops$: git add . && git commit -m "Fixed bug" [dev b6322b2] Fixed bug 1 file changed, 1 insertion(+) create mode 100644 version01.txt #adding next change ubuntu@~/Devops$: echo "This is new feature with minor alterations" >> version01.txt #stage and commit the change ubuntu@~/Devops$: git add . && git commit -m "Added feature2.1" [dev 5f34f31] Added feature2.1 1 file changed, 1 insertion(+) #adding next change ubuntu@~/Devops$: echo "This is advancement of the previous feature" >> version01.txt #stage and commit the change ubuntu@~/Devops$: git add . && git commit -m "Added feature2.2" [dev c514680] Added feature2.2 1 file changed, 1 insertion(+) #adding final change ubuntu@~/Devops$: echo "Feature 2 is ready for releas" >> version01.txt #stage and commit the change ubuntu@~/Devops$: git add . && git commit -m "Feature 2 completed" [dev 52cfe87] Feature 2 completed 1 file changed, 1 insertion(+) #view the contents ubuntu@~/Devops$: cat version01.txt This is the bug fix in development branch This is new feature with minor alterations This is advancement of the previous feature Feature 2 is ready for releas #view the commit history ubuntu@~/Devops$: git log --oneline 52cfe87 (HEAD -> dev) Feature 2 completed c514680 Added feature2.2 5f34f31 Added feature2.1 b6322b2 Fixed bug 7245537 Completed feature 1 119ada7 Fixed a bug dc8e16c Revert "Added new feature" 3d2230b Fixed bug in feature 2 2b1b7ce Added feature 3 d7a3075 Added feature 2 df4abbb (origin/dev) Added new feature 23ac50c Added day 2 file 0983dd8 Initial commit
Now that we have added changes to the "dev" branch, we proceed to add them to a new "prod" (production) branch using git rebase
#switch to "main" branch
ubuntu@~/Devops$: git checkout main
Switched to branch 'main'
Your branch is up to date with 'origin/main'.
#create and switch to new "prod" branch
ubuntu@~/Devops$: git checkout -b prod
Switched to a new branch 'prod'
#check the commit history before rebase
ubuntu@~/Devops$: git log --oneline
27f73f4 (HEAD -> prod, origin/main, origin/HEAD, main, feature) Fixed a bug in feature
545c4f4 Added new feature
3d2230b Fixed bug in feature 2
2b1b7ce Added feature 3
d7a3075 Added feature 2
df4abbb (origin/dev) Added new feature
23ac50c Added day 2 file
0983dd8 Initial commit
#merge the changes from "dev" branch to "prod" using git rebase
ubuntu@~/Devops$: git rebase dev
Successfully rebased and updated refs/heads/prod.
ubuntu@~/Devops$: git log --oneline
27f73f4 (HEAD -> prod) Fixed a bug in feature
545c4f4 Added new feature
52cfe87 (dev) Feature 2 completed
c514680 Added feature2.2
5f34f31 Added feature2.1
b6322b2 Fixed bug
7245537 Completed feature 1
119ada7 Fixed a bug
dc8e16c Revert "Added new feature"
3d2230b Fixed bug in feature 2
2b1b7ce Added feature 3
d7a3075 Added feature 2
df4abbb (origin/dev) Added new feature
23ac50c Added day 2 file
0983dd8 Initial commit
Task 3 - git cherry-pick
In the Production branch Cherry pick Commit “Added feature2.2 in development branch” and add the below lines in it:
The line to be added after Line3>> This is the advancement of the previous feature
Line 4>>Added a few more changes to make it more optimized.
Commit: Optimized the feature
We will use git cherry-pick
command to pick a commit from the "dev" branch to the "prod" branch and then commit a few changes to the "prod" branch.
#switch to "prod" branch
ubuntu@~/Devops$: git checkout prod
Already on 'prod'
#view the commit log to get the commit id hash
ubuntu@~/Devops$: git log --oneline
27f73f4 (HEAD -> prod) Fixed a bug in feature
545c4f4 Added new feature
52cfe87 (dev) Feature 2 completed
c514680 Added feature2.2
5f34f31 Added feature2.1
b6322b2 Fixed bug
7245537 Completed feature 1
119ada7 Fixed a bug
dc8e16c Revert "Added new feature"
3d2230b Fixed bug in feature 2
2b1b7ce Added feature 3
d7a3075 Added feature 2
df4abbb (origin/dev) Added new feature
23ac50c Added day 2 file
0983dd8 Initial commit
#cherry-pick the required commit
ubuntu@~/Devops$: git cherry-pick c514680
Auto-merging version01.txt
CONFLICT (content): Merge conflict in version01.txt
error: could not apply c514680... Added feature2.2
hint: After resolving the conflicts, mark them with
hint: "git add/rm <pathspec>", then run
hint: "git cherry-pick --continue".
hint: You can instead skip this commit with "git cherry-pick --skip".
hint: To abort and get back to the state before "git cherry-pick",
hint: run "git cherry-pick --abort".
#view status of the repo for conflicts
ubuntu@~/Devops$: git status
On branch prod
You are currently cherry-picking commit c514680.
(fix conflicts and run "git cherry-pick --continue")
(use "git cherry-pick --skip" to skip this patch)
(use "git cherry-pick --abort" to cancel the cherry-pick operation)
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: version01.txt
no changes added to commit (use "git add" and/or "git commit -a")
#update the file contents and resolve the conflict
ubuntu@~/Devops$: vim version01.txt
#continue with cherry-pick
ubuntu@~/Devops$: git cherry-pick --continue
U version01.txt
error: Committing is not possible because you have unmerged files.
hint: Fix them up in the work tree, and then use 'git add/rm <file>'
hint: as appropriate to mark resolution and make a commit.
fatal: Exiting because of an unresolved conflict.
#the error says to use git add
ubuntu@~/Devops$: git add .
ubuntu@~/Devops$: git status
On branch prod
You are currently cherry-picking commit c514680.
(all conflicts fixed: run "git cherry-pick --continue")
(use "git cherry-pick --skip" to skip this patch)
(use "git cherry-pick --abort" to cancel the cherry-pick operation)
Changes to be committed:
modified: version01.txt
#finally continue cherry-pick
ubuntu@~/Devops$: git cherry-pick --continue
[prod 1171db7] Added feature2.2
Date: Fri Aug 18 03:40:51 2023 +0000
1 file changed, 1 deletion(-)
#view the commit log to verify the cherry-pick commit
ubuntu@~/Devops$: git log --oneline
1171db7 (HEAD -> prod) Added feature2.2
27f73f4 Fixed a bug in feature
545c4f4 Added new feature
52cfe87 (dev) Feature 2 completed
c514680 Added feature2.2
5f34f31 Added feature2.1
b6322b2 Fixed bug
7245537 Completed feature 1
119ada7 Fixed a bug
dc8e16c Revert "Added new feature"
3d2230b Fixed bug in feature 2
2b1b7ce Added feature 3
d7a3075 Added feature 2
df4abbb (origin/dev) Added new feature
23ac50c Added day 2 file
0983dd8 Initial commit
#continue further by adding more changes
ubuntu@~/Devops$: echo "This is the advancement of previous feature" >> version01.txt
ubuntu@~/Devops$: echo "Added few more changes to make it more optimized" >> version01.txt
#stage and commit the changes
ubuntu@~/Devops$: git add . && git commit -m "Optimized the feature"
[prod ea6dbfa] Optimized the feature
1 file changed, 2 insertions(+)
Conclusion
In conclusion, Day 11 of the 90DaysDevOps Challenge equips you with advanced Git techniques: git stash
, git cherrypick
, and conflict resolution. These skills streamline moving changes between branches and mastering conflict resolution. Your ability to navigate complex version control scenarios has taken a leap. As you continue your DevOps journey, practice and apply these skills for smoother development.
"🌱 Keep learning, and spread the knowledge to inspire others. 🚀💡"