Using Git Professionally

  • |
  • 19 September 2023
Post image

In this long blog, we are going to improve our git knowledge. I will try to explain the some concepts behing the basic things in git. Writing perfect commint, choosing branch strategy, merge vs rebase etc …

Let’s start with commits. It has two parts:

  • Adding the right changes
  • Compose a good message

Perfect commit

1. Add the right changes

  1. Add the right changes!
  2. Compose a good commit message

We should create a commit that makes sense which includes changes from a single commit

We shouldn’t create only one commit with includes all changed files :

/git/bad_commit.png

It is better to separate commits like this:

/git/better_to_separate.png

This also separates different topics. With this way it will be also easy to understand both for you and your colleagues.

We can also commit part of the changes from a specific file using -p tag in the git add command. Let’s say we want to separate changes in the file-2 into two different commits:

/git/separate_in_one_file.png

Assume that you have the following index.html file:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <a href="/about">About</a>
  <a href="/blog">Blog</a>
</body>
</html>

And assume that you have added two more links:

<!DOCTYPE html>
<!-- ... -->
<!-- ... -->
<body>
  <a href="/about">About</a>
  <a href="/blog">Blog</a>
  <a href="/contact-us">ContactUs</a>
  <a href="/login">Login</a>
</body>
</html>

And also assume that after you run the git status you see the following changes:

--- a/index.html
+++ b/index.html
@@ -8,5 +8,7 @@
 <body>
   <a href="/about">About</a>
   <a href="/blog">Blog</a>
+  <a href="/contact-us">ContactUs</a>
+  <a href="/login">Login</a>
 </body>
 </html>
\ No newline at end of file

Now in this case parts of the index.html should be included in the “contact-us commit” and others part will be left to “login commit”

We can separate commit for the index.html using -p (adding commit in patch level)

# with this command 
# git will ask whether we want to add each changes into the commit or not
git add -p index.html 

--- a/index.html
+++ b/index.html
@@ -8,5 +8,7 @@
 <body>
   <a href="/about">About</a>
   <a href="/blog">Blog</a>
+  <a href="/contact-us">ContactUs</a>
 </body>
 </html>
\ No newline at end of file
(1/1) Stage this hunk [y,n,q,a,d,e,?]? y

Now let’s talk about commit messages

2. Commit message

Our commit message in general has two characteristics:

  • Subject: concise summary of what happened
  • Body: More detailed explanation:
    • What is now different than before?
    • What is the reason for the change?
    • Is there anything to watch out for?

Let’s say we are adding re-captcha to our login form. And we are going to write our commit message. Here is the example:

Note: After adding one empty space, git knows that we are going to write body of the commit message

Add recaptcha for login form

Login form now requires a recaptcha to be completed:
- login.html uses google recaptcha library
- invalid signup attempts are now blocked

Branching strategies

  • Git only allows us to create branches, it doesn’t tell us how to use them
  • There is no golden rule for branching. It highly depends on your team and company.

Mainline development

  • In this strategy, you should always be integrating changes into your main branch (with branches includes relatively small commits)
  • In this strategy, there are a few branches and easy to follow
  • And each branches should be tested (should match with QA standards)
/git/main_development.png

State, Release and Feature Branches

This strategy includes different types of branches. Some branches are short-lived such as feature branch, and some of them are long-lived such main.

And when there are changes in main branch, we should update our feature or other branches as well

/git/different_branch_type.png

Choosing the right strategy depends on the requirements. I can’t say that one of them is better than other. Before leaving this topic let me introduce the popular branching strategies GithubFlow & GitFlow

GithubFlow

GitHub’s workflow is set up so that there is one default branch that serves as the main project and individual branches where team members can work on adding new features without compromising the default branch.

There are no branch types like bug or etc.. only features and main branch

/git/github_flow.png

GitFlow

  • This flow involves the use of feature branches and multiple primary branches.
  • It has numerous, longer-lived branches and larger commits.
  • In this model, developers create a feature branch and merging it to the main branch until the feature is complete. (feature branch not directly integrated with main)
  • This flow can be used for projects that have a scheduled release cycle.
  • There are 2 long-lived branches called main & develop
  • The other branches such as feature can’t be merged directly into main, it merged into the develop then merged into the main with version number

Pull requests

Actually you don’t have to create any pull request while adding your implementation into your main branch, you may directly add your implementation.

Main requirement for the pull request is to review code. Because directly adding new features into main(or master) can be harmful. It is better to review and getting feedback what you did from others perspective. That’s the main reason for the pull requests.

It is also useful when you don’t have direct access the main branch. This is the another use case.

Let’s say we want to add new feature in spring boot project. However, we don’t add our implementation directly into the main branch of spring boot project. Instead we can fork the repo then create branch then add our changes then push the changes.

Forking means that we are just creating local copy for the given repository

Github repo for spring boot project https://github.com/spring-projects/spring-boot

/git/fork_repository.png

Here is the step by step example:

  1. A developer initiates a “fork” of an “official” server-side repository, effectively establishing their personal server-side copy.
  2. Subsequently, the developer clones the newly created server-side copy to their local system.
  3. They augment the local clone by adding a Git remote path for the “official” repository.
  4. Within this local clone, a fresh feature branch is crafted.
  5. On this newly created branch, the developer introduces changes and commits them.
  6. As a result of these changes, new commits are generated.
  7. The developer proceeds to push the branch, along with its new commits, to their individual server-side copy.
  8. The final step involves initiating a pull request, where the developer requests to merge their new branch into the “official” repository.
  9. Following review and approval, the pull request is merged into the original server-side repository, thereby incorporating the changes.

Lastly, let’s also talk about merge vs rebase

Merge vs Rebase

Both merge and rebase are designed to integrate changes from one branch into another branch.They just do it in very different ways.

It is better to understand with graphs. Let’s say we created new feature branch:

/git/merge_rebase_base.png

Right now let’s say we want to update master with our feature branch using merge command.

git merge featureBranch

To do that basically we run the following commands:

$ git checkout master
$ git merge feature

After merge command here is the history in master:

/git/after_merge.png
  • In git merge, new commit has been created which represents the integration of changes.

  • Advantages:

    • Presevers the entire commit history of both branches
    • Clearly indicates when and where the integration of changes occurred.
  • Disadvantages:

    • Clutters the commit history with merge commits, which can make it harder to follow the history.
    • May lead to a non-linear history when multiple feature branches are merged.

git rebase master

To do that basically we run the following commands: (to rebase the feature branch onto master, we have to run the following command on the feature branch)

git checkout feature
git rebase master
  • Moves the entire feature branch to begin on the tip of the main branch
  • In other words, effectively rewrites the commit history of the branch being rebased.
/git/after_rebase.png
  • Advantages:
    • Produces a linear, cleaner commit history.
    • Helps avoid unnecessary merge commits
    • Makes it easier to identify when features were added
  • Disadvantages:
    • Rewriting history can be problematic if others are collaborating on the same branch, as it creates new commit hashes.
    • Can be more complex to resolve conflicts during a rebase.

Choosing Between Merge and Rebase

  • Use merge when you want to preserve the individual history of feature branches and want to clearly show when branches were merged.
  • Use rebase when you want a cleaner, linear history and are working on a feature branch individually or in a collaborative environment where you can coordinate rebases.

You May Also Like