- Initialize a Git Repository: Create a new directory called
git-story
. Navigate into this directory and initialize a Git repository usinggit init
.
-
Create
chapter1.txt
: Write the following paragraph in a new file namedchapter1.txt
:Once upon a time, in a village named "Init", lived a brave adventurer named Git.
Add the file to the staging area with
git add chapter1.txt
. -
Commit Your Changes: Commit the changes with the message "Chapter 1: Introduce Git from Init village" using
git commit -m "Chapter 1: Introduce Git from Init village"
. -
Check the Status: Use
git status
to check the status of your working directory and staging area. -
Show the Last Commit: Use
git show
to display the changes made in the last commit. -
Check the Commit History: Use
git log
to view the commit history. Try usinggit log --oneline
to see a simplified one-line-per-commit version of the log.
-
Modify
chapter1.txt
: Add more to the story inchapter1.txt
. For example, add the following lines:Git was well-known for his curiosity and eagerness to learn. He had one dream - to explore the mysterious world of Version Control.
Stage the changes with
git add chapter1.txt
. -
Commit the Changes: Commit the changes with the message "Chapter 1: Extended story" using
git commit -m "Chapter 1: Extended story"
. -
Show and Log: Use
git show
again to display the most recent commit. Then, usegit log
to see the new commit history.
By this point, you've used git init
, git add
, git commit
, git status
, git show
, and git log
in a realistic workflow.
-
Create a New Branch: Create a new branch called
chapter2
usinggit branch chapter2
, and switch to it usinggit checkout chapter2
. -
Create
chapter2.txt
: Write a short paragraph about Git's decision to leave Init to explore the world of Version Control inchapter2.txt
. For instance:Chapter 2: Git's Decision Git, after much contemplation, decided to leave his home in Init to explore the vast world of Version Control.
Stage and commit the changes with the message "Chapter 2: Git's decision".
-
Switch and Merge: Switch back to the
master
branch usinggit checkout master
. Merge thechapter2
branch intomaster
usinggit merge chapter2
. This should be a fast-forward merge as there are no new commits onmaster
thatchapter2
doesn't have. -
Non-Fast-Forward Merge: Let's introduce a scenario where a fast-forward merge isn't possible. First, create and switch to a new branch
chapter3
and createchapter3.txt
with content:Chapter 3: Git's First Step into the World of Version Control With a heavy heart and a bag full of hopes, Git took his first step into the world unknown.
Commit this change. Now, switch back to
master
, modifychapter1.txt
orchapter2.txt
, and commit this change. Try mergingchapter3
intomaster
. This will be a non-fast-forward merge as there have been new commits on bothmaster
andchapter3
. -
Simulate Merge Conflicts: To create a merge conflict scenario, let's create a new branch from
master
calledconflict_branch
. In this branch, change the first line ofchapter1.txt
to:Once upon a time, in a town named "Init", lived an enthusiastic explorer named Git.
Commit the changes. Now switch back to
master
and change the same line to:Once upon a time, in a town named "Init", lived a brave adventurer named Git.
Commit the changes here too. Now, try to merge
conflict_branch
intomaster
. This should result in a merge conflict. Solve this conflict, decide which version to keep, stage, and commit the merge.
By the end of this part, you would have practiced creating branches (git branch
), switching branches (git checkout
), merging branches (git merge
), resolving conflicts, and experienced both fast-forward and non-fast-forward merges.
-
Log and Diff: First, add more details to
chapter2.txt
, such as:His family and friends gathered to bid him farewell. It was an emotional moment, but Git was determined.
But don't stage or commit them yet. Run
git diff
to see the un-staged changes in your working directory. -
Explore Git Log Options: Now, commit the changes with the message "Chapter 2: Extended story". After committing, check the log with
git log --stat
to see statistics for files modified in each commit. You can also usegit log -p
to see the patch output, i.e., the actual changes in detail.
-
Stashing Changes: Now, make some changes to
chapter3.txt
. For example:Git was ready for his journey, prepared to face the trials ahead.
But don't stage or commit these changes. Suppose you need to switch to another task, and you want to save these changes without committing them. Use
git stash save "WIP on chapter3"
to stash these changes. -
List the Stashes: You can list all the stashes using
git stash list
. -
Apply a Stash: Finish your other task and come back to this one. Apply the changes you stashed earlier using
git stash apply stash@{0}
. This applies the stash and keeps it in the stash list for later use. -
Pop a Stash: You can also use
git stash pop stash@{0}
to apply the stash and remove it from the stash list. -
Drop a Stash: If you want to delete a stash without applying it, you can use
git stash drop stash@{0}
.
-
Create a New Repository on GitHub: First, go to GitHub and create a new repository. Name it, for example, "git-story".
-
Connect your Local Repository to the Remote Repository: Navigate to your local Git project (in this case, the "git-story" directory). Run the following command to connect your local repository to the remote repository you just created on GitHub:
git remote add origin <YOUR_REMOTE_REPOSITORY_URL>
Replace
<YOUR_REMOTE_REPOSITORY_URL>
with the URL of the repository you created on GitHub. You can find this URL on your GitHub repository page. -
Verify the Remote Repository: Confirm that the remote repository has been set up with the following command:
git remote -v
This will list the URLs of the remote repositories associated with your local repository.
-
Push Local Commits to the Remote Repository: Now push the commits from your local repository to your remote repository on GitHub with this command:
git push -u origin master
This command pushes your
master
branch to the remote repository namedorigin
. The-u
option remembers the settings, so next time, you can simply rungit push
. -
View Your Project on GitHub: Open your browser and go to your repository on GitHub. You should see all the files you've worked on along with the commit messages.
By the end of this section, you've learned how to push your local Git repository to a remote repository on GitHub, making it accessible from any computer and allowing you to collaborate with others.
-
Interactive Rebase: Let's say you made a series of minor commits to
chapter3.txt
and now realize that they should've been one single commit. The following lines were added in separate commits: Now, usegit rebase -i HEAD~3
to squash these commits into one.Git took a deep breath as he stepped into the world of Version Control. He knew the journey wouldn't be easy, but he was ready. He was doing this for his village, for Init.
By the end of this part, you would have learned how to use git diff
to view changes, git stash
to save work in progress, git log
with different options to view detailed commit history, and git rebase -i
for interactive rebasing.
- Editing Commit Messages with Interactive Rebase: Let's say the commit message of the third commit from the top is not informative enough. You can change it using interactive rebase. Run
git rebase -i HEAD~3
and replacepick
withreword
for the third commit. Save and exit the text editor. You will be prompted to enter a new commit message. - Reordering Commits with Interactive Rebase: Imagine that you committed some changes to
chapter2.txt
after making several commits tochapter3.txt
. The story would make more sense if all commits forchapter2.txt
were together. You can use interactive rebase to reorder commits. Rungit rebase -i HEAD~5
(or however many commits you need to go back) and reorder the lines to change the order of commits. - Dropping Commits with Interactive Rebase: Suppose you made a commit that is no longer necessary. You can remove it using interactive rebase. Run
git rebase -i HEAD~4
, replacepick
withdrop
(or simply delete the line), and then save and exit. - Squashing Commits with Interactive Rebase: If you have multiple commits that should be one commit (similar to the example given before), you can squash them using interactive rebase. Suppose you made three commits to
chapter3.txt
where each commit added one line. You can squash these into one commit. Rungit rebase -i HEAD~3
, replacepick
withsquash
ors
for the last two commits, save and exit. You will then be prompted to create a new commit message for the squashed commits.
Interactive rebasing can be a powerful tool when used correctly, as it allows you to change your commit history in many ways. However, be careful when using it, as it can also easily lead to confusion or data loss if not used correctly. It's especially important to be cautious when rebasing commits that have been pushed to a shared repository.
-
Merge Workflow: You've been working on the
master
branch, adding more to the adventures of Git. Now, you've learned about a new place called "The Hub" and decide to create a new chapter about it. You start working on this new chapter by creating a new branchchapter_hub
frommaster
and addchapter_hub.txt
with a few lines:Chapter Hub: The Adventure in the Hub Git had heard tales of a mystical place called "The Hub", where many adventurers gathered and shared their journeys.
Commit this change.
-
Changes on Master: Meanwhile, there are some changes made on
master
. Go back tomaster
and add some more lines tochapter1.txt
. Commit these changes. -
Merge Master: Now, you want to include the changes made in
master
in yourchapter_hub
branch. One way to do this is usinggit merge
. Switch tochapter_hub
and mergemaster
into it (git merge master
). Notice the merge commit created and how it brings together the two branches. -
Rebase Workflow: Let's introduce a similar scenario, but this time, use
git rebase
instead. Create a new branchchapter_fork
frommaster
and addchapter_fork.txt
with a few lines. Commit this change. -
Changes on Master (Again): Again, make some changes on
master
- perhaps more additions tochapter1.txt
. Commit these changes. -
Rebase Master: Now, you want these changes from
master
in yourchapter_fork
branch. This time, usegit rebase master
while onchapter_fork
. This moves the entirechapter_fork
branch to begin on the tip of themaster
branch.
In both workflows, you have accomplished the same end result: bringing changes from master
into your feature branch. But the way history is recorded is different.
With git merge
, your feature branch will retain its commit history, and a new merge commit is created. This preserves the context of the branch and its independence from the master
's timeline, but can make the history look complex.
On the other hand, git rebase
moves your feature branch's changes onto the tip of master
, making it appear as if you've created your feature branch from the latest master
commit. This makes a linear and clean history, but it rewrites the commit history and removes the context of when the feature branch was originally created.
It's also worth mentioning that git merge
is safe to use on public branches, while git rebase
should be used with care as it rewrites history. It's better to use git rebase
on local branches that haven't been shared with others.
-
Amend Commit: Add a line to
chapter1.txt
:Git was eager to explore new places.
Commit this change with the message "Chpater 1: Git's eagerness". Now, notice that "Chapter" has been misspelled in the commit message. Correct it using
git commit --amend
. This command will open the text editor allowing you to modify the last commit message. Change "Chpater" to "Chapter" and save the file. -
Revert Commit: Add another line to
chapter2.txt
:Little did Git know, the adventures ahead were tougher than expected.
Commit this change with the message "Chapter 2: The tough journey ahead". After committing, you decide these changes are not suitable for the chapter. Use
git log
to find the SHA-1 hash of the commit you want to revert, and then usegit revert <commit-hash>
. This will create a new commit that undoes the changes made in the commit you're reverting. -
Reset Commit: Now add a line in
chapter3.txt
:Git, however, was confident in his abilities.
Commit this change with the message "Chapter 3: Git's confidence". Soon after, you realize this change contradicts Git's character development. You decide you want to completely remove this commit from your history. Use
git reset --hard HEAD~1
to delete the last commit. Be careful, as this will permanently delete the commit and its changes. -
Cherry-Pick Commit: On
master
, add this line tochapter2.txt
:Back home, Git's family and friends were hoping for his safe return.
Commit this change with the message "Chapter 2: Thoughts from home". You realize this information is also relevant to the new branch
chapter_fork
you're working on. Switch tochapter_fork
branch, and usegit cherry-pick <commit-hash>
to apply this commit frommaster
tochapter_fork
.
By the end of this part, you will have learned how to modify the last commit using git commit --amend
, how to undo changes using git revert
, how to permanently delete commits using git reset
, and how to apply changes from another branch using git cherry-pick
.
-
Exploring Reflog: Run
git reflog
to see a list of operations that updated HEAD. This includes every commit, amendment, cherry-pick, checkout, rebase, etc. you've performed. It's a great tool to understand the movement of the HEAD pointer and can serve as a safety net if you need to recover lost commits. -
Recovering Lost Commit: Now, let's create a scenario to recover a lost commit. Add another line to
chapter3.txt
:The first challenge Git faced was understanding the concepts of Version Control.
Commit this change with the message "Chapter 3: Git's first challenge".
-
Losing the Commit: Now, assume that you accidentally hard reset the last two commits by running
git reset --hard HEAD~2
. Runninggit log
won't show the last two commits now. But you realize you need the commit "Chapter 3: Git's first challenge" back. -
Recovering the Commit: Here,
git reflog
comes to the rescue. Rungit reflog
and find the SHA-1 hash of the commit "Chapter 3: Git's first challenge". Then, usegit cherry-pick <commit-hash>
to apply that commit back to your current HEAD.
Remember, git reflog
is a lifesaver when you've lost commits. It should be used with caution, but can save you in a pinch when you need to recover lost work.
-
Introduction to Git Hooks: Git hooks are scripts that Git executes before or after events such as commit, push, and receive. Git hooks are a built-in feature - no need to download anything. Git hooks are run locally.
-
commit-msg Hook: Let's create a simple pre-commit hook that checks if the commit message follows a certain format. First, navigate to the
.git/hooks
directory in your repository. Create a new file calledcommit-msg
(no file extension). -
Writing the Hook: In the
commit-msg
file, add the following code:#!/bin/sh COMMIT_MSG_FILE=$1 COMMIT_MSG_CONTENT=$(cat "$COMMIT_MSG_FILE") if [[ ! "$COMMIT_MSG_CONTENT" =~ ^Chapter\ [1-9][0-9]*:.*$ ]]; then echo "ERROR: Commit message does not follow the standard - 'Chapter X: ...'" exit 1 fi
This code will prevent any commit that does not have a message starting with "Chapter X: ...". Save the file and make it executable using
chmod +x commit-msg
. -
Testing the Hook: Try committing a change with a message that doesn't follow the correct format. The commit should be rejected. Now, commit with a message that does follow the format, like "Chapter 4: Git's Victory". The commit should be successful.
Remember, these hooks are not committed with the rest of your project and reside on your local machine only. If you want to share these hooks with your team or make them part of the project, you'll need to add them to the project's directory and then create a setup script or mention in your documentation that these scripts need to be copied to .git/hooks/
to be activated.
These hooks can be powerful tools for enforcing certain rules, but be careful as they can also be disruptive to the workflow if used incorrectly.
- Introduction to Git Bisect:
git bisect
is a powerful tool for finding the commit that introduced a bug in your project. Let's simulate a situation where you have to use it. Imagine you have been adding new features and making many changes to your story over the past weeks. Today, you've realized that a critical error (a typo) has crept intochapter1.txt
at some point, but you're not sure when. - Starting Bisect: First, ensure that you are on the branch where the error exists (let's assume
master
for now). Start the bisect process usinggit bisect start
. - Bad Commit: Next, mark the current commit as bad since the error exists in this commit. Use
git bisect bad
. - Good Commit: Now, you need to find a commit where you're sure the error didn't exist. For our case, let's assume it's the commit where
chapter1.txt
was first created. Usegit log
and find the commit hash where you addedchapter1.txt
. Mark that commit as good usinggit bisect good <commit-hash>
. - Bisect Process: Git will now checkout a commit halfway between the good and bad commits. Check
chapter1.txt
and see if the typo exists. If it does, mark this commit as bad usinggit bisect bad
. If the error does not exist in this commit, mark it as good usinggit bisect good
. - Repeat Process: Repeat the process until Git pinpoints the exact commit that introduced the error. Once that happens, Git will display something like:
xxxxxxx is the first bad commit
. - Exit Bisect: Now that you've found the offending commit, end the bisect session using
git bisect reset
. This will take you back to where you were before starting the bisect process.
Remember, git bisect
is a very powerful tool when you're trying to find the commit that introduced a bug in your project. It works on the principle of binary search, efficiently narrowing down the problematic commit