Version Control Systems
Version Control Systems
Version Control Systems are software tools that help manage changes to source code
over time. They keep track of every modification to the code in a special kind of
database. If a mistake is made, developers can turn back the clock and compare
earlier versions of the code to help fix the mistake while minimizing disruption to all
team members.
Why VCS
1. Collaboration: Allows multiple people to work on the same codebase
simultaneously without overwriting each other's work.
2. Track Changes: Keeps a comprehensive history of project changes, including
who made what changes and when providing a clear audit trail.
3. Revert and Compare: Enables the ability to revert to previous project versions,
which is crucial for troubleshooting and understanding project evolution.
4. Branching and Merging: Facilitates concurrent development by allowing
branching (creating another line of development) and merging (bringing
different lines of development together).
Pros
● Simplicity: Easier to understand for beginners.
● Centralized Control: Administrators have fine-grained control over the
repository.
Cons
● Single Point of Failure: No one can collaborate or save versioned changes if the
central server fails.
● Limited Offline Capabilities: Requires connection to the central server for
most operations.
Pros
● Full Repository Backup: Every checkout is a full backup of the repository.
● Enhanced Collaboration: Enables more complex workflows and offline work.
Cons
● Complexity: Can be more complex to understand and manage, especially for
beginners.
● Larger Repository Size: As each clone is a full copy of the repository, it can take
up more space.
Git
Git is a version control system that allows developers to track changes made to their
codebase over time. It was created in 2005 by Linus Torvalds, the founder of Linux,
as a more powerful and efficient alternative to existing version control systems.
Git Init
The git init command is a fundamental tool in Git, used to initialize a new Git
repository or reinitialize an existing one.
Usage
Syntax:
git init [repository name]
This creates a new subdirectory named .git that houses all of your necessary
repository files. This .git directory is what makes your directory a Git repository and
tracks the history and configuration.
Purpose
1. Organizing Commits: It allows you to craft your commits with precision. You
can choose exactly which changes make up a commit.
2. Partial Commits: This area facilitates partial commits, enabling you to stage
only certain parts of the changes made in the working directory.
How It Works
When you make changes in your working directory, Git sees them as "modified."
Using git add, these changes move to the staging area.
Once staged, changes are marked as "staged" and are ready to be committed.
The git status command is useful to see which files are in which state (modified,
staged, etc.).
Syntax:
git add [file or directory]
Adding a Single File: To stage a specific file, use git add filename.
Adding Multiple Files: You can add multiple files by listing them: git add file1 file2.
Adding All Changes: To add all new and modified files, use git add . or git add -A.
This stages all changes in the current directory and its subdirectories.
Git Commit
When you run git commit, Git takes the changes that are in the staging area and
saves them into the repository. When you make a commit, it is added to the end of
the current latest commit.
Syntax:
git commit -m "Your message here"
Key Features
1. Immutability: Each commit is a permanent snapshot of the repository at a
given point in time. This snapshot includes references to all the files as they
existed at that moment.
2. Unique Identifier: Each commit is identified by a unique SHA-1 hash. This
allows tracking and referencing specific commits in the repository history.
Best Practices
1. Logical Changes: Commit related changes together. Avoid including unrelated
changes in the same commit.
2. Frequent Commits: Commit often to capture stages of development. Smaller,
more frequent commits make it easier to identify where and when changes
were made.
3. Testing Before Committing: Ensure your code works as expected before
committing. This helps in maintaining a stable project history.
Understanding Commits
1. Commits in History: Commits are a part of the branch history. When you
change branches, the commits that are part of that branch's history become
the current state of the project.
2. Relationship with Branches: When you make a commit, it is added to the end
of the current branch. This commit then becomes the latest snapshot on that
branch.
3. Immutability of a Git Commit
Immutable Nature:
Once a commit is created in Git, it becomes an immutable snapshot of the repository
at a certain point in time. This means that the commit itself cannot be altered or
changed without generating a new commit.
Implications of Immutability
1. Data Integrity: The immutability of commits ensures data integrity and
traceability within the repository. It allows Git to detect changes or corruption
in the repository history.
2. Traceability and Accountability: The immutable nature of commits allows for a
reliable and traceable history of changes, which is crucial for collaboration and
version control.
3. Amending Commits: While you can't change a commit once it's made, Git
allows you to "amend" a commit. However, amending actually creates a new
commit with a new SHA-1 hash, replacing the old commit in the project
history.
Git Branching
Multiple developers might be working on independent features at the same time. The
primary purpose of branching is to diverge from the main line of development and
continue to work independently without affecting the main line. Once the
independent work is finished, you can converge the branches (merging back into the
main branch). Branches serve as pointers to the latest commit. When you make a
new commit, the branch pointer moves forward automatically. Creating a new
branch in Git is a very quick and simple process as it is just creating a variable name.
Creating a New Branch: Use git branch [branch-name] to create a new branch.
Switching Branches: Use git checkout [branch-name] to switch to an existing branch.
Create and switch to a New Branch: git checkout -b [branch-name]
Branching Strategies
1. Feature Branching: Creating a branch for each new feature or bug fix keeps
the work organized and separated until it’s ready to be merged.
2. Release Branching: This involves creating a branch for release preparation,
allowing for bug fixes and preparation without disrupting the main
development work.
3. Long-Running Branches: Typically, a project has long-running branches like
master or develop, which are stable versions of the project at different stages.
Best Practices
1. Regular Commits and Mergers: Regularly commit your changes within your
branch and merge frequently from the main branch to minimize merge
conflicts.
2. Naming Conventions: Use clear, descriptive names for branches to easily
identify their purpose.
3. Cleanup: Delete branches after their changes have been merged to keep the
repository clean and manageable.
Git Merge
Merging in Git is a way to combine changes from different branches. It's like taking
the work from one branch and adding it to another. For example, you might develop
a new feature in a separate branch and then merge it into the main branch once it's
ready.
How to Merge
1. Choose the Target Branch: First, switch to the branch where you want the
changes to go. For example, git checkout main.
2. Merge Another Branch: Then, use git merge [branch-name] to merge changes
from [branch-name] into your current branch.
Fast-Forward Merge
A fast-forward merge in Git is like having a straight road from your feature branch
back to the main road. This occurs when there have been no new changes on the main
branch since you started your feature branch. If nothing new has been added there,
git can simply move the end of the main branch up to the end of your feature branch.
Your changes are now part of the main branch. There won’t be merge conflicts in this
case.
Three-Way Merge
A three-way merge happens when there have been other changes on the main branch
while you were working on your feature. In this case, your side branch doesn’t
directly fit onto the end of the main branch anymore. To merge your changes with
the main branch, Git uses a special process. It looks at three points: the end of your
feature branch, the end of the main branch, and where both branches were last the
same. Git then figures out how to combine these changes. In this case, there can be
merge conflicts that you will have to resolve.
Squash Merge
Squash merging is a way to combine all the changes from a feature branch into a
single commit when you merge it into another branch. It's useful for keeping your
history clean and organized. Instead of having many small commits from a feature
branch, you have one concise commit.
Syntax: Use git merge --squash [feature-branch]. This combines all changes into a
single commit. After this, you'll need to commit these changes manually with git
commit.
Git Rebase
In Git, rebase is a command that helps to move or combine a sequence of commits to
a new base commit. It's a way to rearrange the history of your commits for a cleaner,
more linear progression.
Why Rebase
1. Clean History: Rebase is often used to tidy up a series of commits before
merging them into a main branch. It can make the history more readable and
easier to understand.
2. Integrating Changes: Rebasing can also be used to integrate changes from one
branch into another, similar to merging but with a different approach.
How Rebase Works
1. Switch to Your Feature Branch: Start by being in the branch with your new
work, for example, git checkout feature-branch.
2. Start the Rebase: Use git rebase [base-branch], where [base-branch] is the
branch you want to rebase onto (often main or master). Git Re-applies
Commits: Git takes the commits from your feature branch and re-applies
them on top of the latest commit in the base branch. This makes it look like
your changes were made on top of the current state of the base branch.
Benefits of Rebase
1. Linear History: Rebasing creates a linear project history, which can be easier
to follow than the branched history created by merging.
2. Avoiding Extra Merge Commits: It eliminates the need for an extra merge
commit that you get when using git merge.
HEAD in Git
In Git, HEAD is a reference to the current or last commit in the currently checked-
out branch. It can be thought of as a pointer to the current branch or current state of
your repository. As you commit, the HEAD moves forward with each commit. It
always represents the latest commit in the branch.
Detached HEAD State: When you check out a specific commit instead of a branch,
Git enters a 'detached HEAD' state. In this state, HEAD points directly to a commit,
rather than a branch. Changes made in this state aren’t attached to a branch, so they
can be difficult to find after switching back to a branch. It's useful only for temporary
exploration or read-only purposes in a repository. It’s generally a good idea to avoid
making permanent changes while in a detached HEAD state, as these changes can be
lost when switching back to a branch.
HEAD in Branches: When you switch branches with git checkout [branch-name],
HEAD updates to point to the tip of the new branch. If you create a new commit on
this branch, HEAD moves forward with the new commit.
Git Remotes
Git remotes are like offsite backups or mirrors of your local Git repository. They allow
you to collaborate with others, share your code, and work on different versions of
your project simultaneously. Think of them as remote locations where your Git
repository lives. It could be:
1. A remote Git server: like GitHub, GitLab, or Bitbucket.
2. Another developer's local machine.
This creates a local copy of the repository on your computer, including all files and
history.
git remote -v
Fetch Command
To fetch changes from the remote repository without merging them:
git fetch <remote-name>
Pull Command
To update your local repository with changes from the remote:
git pull origin master
Internally, git pull starts with git fetch. This command retrieves commits, files, and
refs from a remote repository into your local repo. Following the fetch, git pull
executes git merge to merge the retrieved branch heads into the current local branch.
As it executes git merge, pull can create a new merge commit as well. Optionally, git
pull can rebase instead of merging. This is done using git pull --rebase This is often a
recommended approach.
Push Command
Push your changes to the remote repository:
git push origin master
Working on Github
Forking Repository
Forking is creating your own copy of a repository. This allows you to freely
experiment with changes without affecting the original project. On the GitHub page
of the repository, click the "Fork" button. This creates a copy under your GitHub
account. Now clone this repository into your local. Use the command git clone [URL
of your fork]. This URL is found on your fork's GitHub page.
Final Merging
Once your PR is approved, the maintainers of the original repository will merge your
changes. Congratulations, your contributions are now part of the project!
Git Cherry-Pick
Git cherry-pick is a powerful tool that allows you to select and apply individual
commits from one branch into another. Unlike merging or rebasing, which typically
apply a series of commits, cherry-picking applies only the commits you specify.
How to Cherry-Pick?
1. Identify the Commit Hash: First, you need the hash of the commit you want to
cherry-pick.
2. Switch to the Target Branch: Use git checkout [target-branch] to switch to the
branch where you want to apply the commit.
3. Cherry-Pick the Commit: Use git cherry-pick [commit-hash]. If the commit
applies cleanly, it will be added to your branch. If there’s a conflict, Git will
pause the cherry-pick operation. Manually resolve the conflicts in your code
editor. After resolving, add the files with git add [file-name]. Complete the
cherry-pick with git cherry-pick --continue.
4. Push the Changes: Use git push origin [target-branch] to push the changes to
the remote repository.