Git best practices ๐Ÿ”€: Git HEAD, useful commands, commit message

What is HEAD in Git?

In Git, HEAD is a pointer to the latest commit in the current branch. It tells Git which commit you’re currently working on.

Types of HEAD States:

  • Normal (Attached HEAD)
    When HEAD points to the latest commit in a branch, it’s called an attached HEAD.
# show commits in single line with message
git log --oneline --graph

* 07cf493 (HEAD -> main, origin/main) feat: Implement tailwind css to product pages
* c3ee7d4 feat: Add images to products
* e342472 feat: Install tailwind css
* 40fc222 first commit
  • Detached HEAD
    If you check out a specific commit (not a branch), HEAD becomes detached.
โœ— git checkout c242462
# undo detached HEAD
โœ— git switch -

# or go back to main
โœ— git checkout main

Common HEAD Uses in Commands

  1. Reset Last Commit (Undo Latest Commit, Keep Changes)
git reset HEAD~1

HEAD~1 means “one commit before HEAD” (previous commit). This unstages the latest commit but keeps changes.

2. Unstage a Staged File

git reset HEAD filename

Removes filename from staged state but keeps changes.

3. Move HEAD to a Different Commit (Soft Reset)

git reset --soft HEAD~2

Moves HEAD back two commits but keeps all changes staged.

4. Hard Reset (Undo Everything, No Recovery)

git reset --hard HEAD~1

Moves HEAD one commit back and deletes all changes.

5. View HEAD Commit Hash

git rev-parse HEAD

Shows the exact commit hash HEAD is pointing to.

HEAD is simply Git’s way of tracking where you are in the commit history. It allows you to navigate, reset, and control commits efficiently.

Git useful commands โœ

# compare 2 commits to see what is changed from one to another
git diff e144462 c4ed9d4

# To ignore all changes in the working directory and reset it to match the latest commit
git restore .

# If You Also Want to Remove Untracked Files
git clean -fd

# completely wipe all changes in the working directory
git restore . && git clean -fd

# move uncommitted file into a special area
git stash

# get back the unstaged files into working tree
git stash pop

# other commands
git stash save "Add tailwind to product show"
git stash list
git stash pop stash@{2}
git stash show
git stash apply

# If you decide you no longer need a particular stash, you can delete it with git stash drop
git stash drop stash@{1}

# or you can delete all of your stashes with:
git stash clear

To get the new changes from the remote repo to your local repo do the following command:

git fetch

but remember this does not update your working directory. git fetch allows you to check the incoming commits using git log and you can merge those changes to your current branch using git merge.

git fetch + git merge = git pull

If all goes well without any code conflict with git pull your code is updated to Local Repo and Working directory. (Your branch is Fast-Forwarded)

 When Do Stash and Pull Interact?

The connection arises in real-world workflows when you need to pull remote changes but you have local uncommitted work:

Scenario:

  1. Youโ€™re working on a branch (main) with uncommitted changes.
  2. You need to pull updates from the remote (git pull), but Git blocks this if your working directory is dirty (has uncommitted changes).
  3. To resolve this, you:
    • Stash your changes (git stash) โ†’ clears the working directory.
    • Pull the updates (git pull).
    • Reapply your stash (git stash pop) to merge your changes with the newly pulled updates.

Git commit message:Best practices ๐Ÿš€

1. Follow the Conventional Format

A well-structured commit message consists of:

  • A short summary (50 characters max)
  • A blank line
  • A detailed description (if necessary, up to 72 characters per line)

Example:

git commit -m "feat: Add user authentication with Devise" -m "Implemented Devise for user authentication, including:
- User sign up, login, and logout
- Email confirmation and password recovery
- Integration with Turbo Streams

Closes #42"

feat: Add user authentication with Devise

Implemented Devise for user authentication, including:
- User sign up, login, and logout
- Email confirmation and password recovery
- Integration with Turbo Streams

Closes #42

Explanation:

  • The first -m argument contains the commit title (short summary, 50 characters max).
  • The second -m argument contains the detailed description, with each bullet point on a new line.
  • The Closes #42 automatically links and closes GitHub/GitLab issue #42 when pushed.

Alternative Using a Text Editor (Recommended for Long Messages)

If your commit message is long, use:

git commit

This opens the default text editor (like Vim or Nano), where you can structure the message properly:

feat: Add user authentication with Devise

Implemented Devise for user authentication, including:
- User sign up, login, and logout
- Email confirmation and password recovery
- Integration with Turbo Streams

Closes #42

This keeps the message clean and readable. ๐Ÿš€

2. Use a Clear and Concise Subject Line

  • Limit the first line to 50 characters.
  • Start with an imperative verb (e.g., “Add”, “Fix”, “Refactor”, “Improve”).
  • Avoid generic messages like “Update” or “Fix bug”.

โœ… Good:

fix: Resolve N+1 query issue in orders controller

โŒ Bad:

Fixed bug

3. Use Conventional Commit Types

Use prefixes to categorize the change:

  • feat: โ†’ New feature
  • fix: โ†’ Bug fix
  • docs: โ†’ Documentation update
  • style: โ†’ Code formatting (no logic change)
  • refactor: โ†’ Code refactoring (no feature change)
  • test: โ†’ Adding/modifying tests
  • chore: โ†’ Maintenance tasks (e.g., dependencies, build scripts)
  • perf: โ†’ Performance improvement
  • ci: โ†’ CI/CD-related changes

Example:

perf: Optimize database queries for dashboard stats

4. Include Context and Motivation

Explain why a change was made if it’s not obvious.

โœ… Good:

refactor: Extract user authentication logic to service object

Moved authentication logic from controllers to a dedicated 
service object to improve testability and maintainability.

5. Reference Issues and PRs

  • Use Closes #123 to automatically close the issue.
  • Use Refs #456 if it’s related but not closing the issue.

Example:

feat: Implement image upload in profile settings

Users can now upload profile pictures. The uploaded images 
are stored using Active Storage.

Closes #89

6. Keep Commits Small and Focused

Each commit should:

  • Represent a single logical change.
  • Avoid mixing refactoring with new features.

โœ… Good:

  • Commit 1: refactor: Extract helper method for API requests
  • Commit 2: feat: Add API endpoint for fetching user statistics

โŒ Bad:

  • Commit 1: feat: Add API endpoint and refactor helper methods

7. Use Present Tense

Write commit messages in present tense, not past tense.

โœ… Good:

fix: Handle nil values in user profile settings

โŒ Bad:

Fixed nil values issue in user profile settings


Following these best practices ensures readable, maintainable, and searchable commit history. ๐Ÿš€