Using Git like a Pro

Using Git like a Pro

Published on
Authors

Git has become the cornerstone of modern software development, enabling teams to collaborate efficiently, track changes, and manage complex projects with ease. This comprehensive guide will take you through essential Git commands and best practices that will elevate your version control skills to a professional level.

Setting Up a New Project

Properly initializing a Git repository sets the foundation for your entire project. Here’s how to start on the right foot:

  1. git init

    • Initializes a new Git repository in the current directory
    • Creates a hidden .git folder to store all Git-related information
    • Best practice: Run this in the root folder of your project

    Example:

    mkdir my-new-project
    cd my-new-project
    git init
  2. git remote add origin <url>

    • Connects your local repository to a remote server
    • Replace <url> with the URL of your remote repository (e.g., from GitHub, GitLab, etc.)
    • Best practice: Use HTTPS URLs for easier authentication, or SSH if you have it set up

    Example:

    git remote add origin https://github.com/username/repo.git
  3. git commit -am "Initial commit"

    • Stages and commits all tracked files with a commit message in one step
    • The -a flag stages all modified files, and -m allows you to provide a commit message
    • Best practice: Make your initial commit message descriptive of the project’s starting point

    Example:

    git commit -am "Initial commit: Project structure and README"
  4. git push -u origin main

    • Pushes the main branch to the remote and sets upstream tracking
    • This establishes a link between your local and remote branches for future pushes
    • Note: Some repositories use master instead of main. Adjust accordingly.

    Example:

    git push -u origin main

    Pro tip: After this, you can simply use git push for subsequent pushes to this branch.

Working with Branches

Effective branching strategies are crucial for organized development and smooth collaboration:

  1. git branch -a

    • Lists all branches, including remote branches
    • Helps you keep track of existing branches in your project
    • The current branch is usually highlighted or marked with an asterisk

    Example output:

    * main
      feature/user-authentication
      remotes/origin/main
      remotes/origin/feature/payment-gateway
  2. git checkout -b feature/new-feature

    • Creates and switches to a new branch for your feature
    • Combines git branch and git checkout into one command
    • Best practice: Use descriptive branch names, prefixed with the type of change (e.g., feature/, bugfix/, hotfix/)

    Example:

    git checkout -b feature/user-profile
  3. git push -u origin feature/new-feature

    • Pushes the new branch to the remote and sets upstream for future pushes
    • Ensures your feature branch is backed up and accessible to collaborators
    • The -u flag sets up tracking, allowing you to use git pull without arguments in the future

    Example:

    git push -u origin feature/user-profile
  4. git branch -d feature/old-feature

    • Deletes a local branch after merging it
    • Use -D instead of -d to force delete an unmerged branch
    • Best practice: Clean up old branches regularly to keep your repository tidy

    Example:

    git branch -d feature/completed-feature

    Pro tip: To delete a remote branch, use: git push origin --delete feature/old-feature

Handling Changes

Efficiently managing your changes is crucial for maintaining a clean project history:

  1. git add -p

    • Stages changes interactively, allowing selection of specific hunks
    • Useful for creating more granular, focused commits
    • Responds to prompts: y (yes), n (no), s (split), e (edit), etc.

    Example workflow:

    git add -p
    # Review each change and decide whether to stage it
    git commit -m "Update user model and fix validation bug"
  2. git commit --amend

    • Modifies the most recent commit, updating the message or adding changes
    • Helpful for fixing small mistakes or improving commit messages
    • Caution: Don’t amend commits that have been pushed to a shared repository

    Example:

    git commit --amend -m "Refactor user authentication and add unit tests"
  3. git reset --hard HEAD~2

    • Rolls back the last two commits, discarding changes locally
    • Use with caution as this operation is destructive and cannot be undone
    • Best used for local cleanup before pushing

    Example:

    git reset --hard HEAD~2
    # This will discard the last two commits and all changes
  4. git stash push -m "Work in progress"

    • Stashes current changes with a custom description
    • Useful for quickly switching contexts without committing incomplete work

    Example:

    git stash push -m "Half-implemented payment feature"
    git checkout main
    # Do some other work
    git checkout feature/payments
    git stash pop

Collaboration

Smooth collaboration is essential in team environments:

  1. git fetch origin

    • Fetches changes from the remote without merging them
    • Allows you to see what’s changed before integrating updates
    • Best practice: Fetch regularly to stay updated with remote changes

    Example:

    git fetch origin
    git log --oneline main..origin/main
    # This shows commits that exist in origin/main but not in your local main
  2. git merge origin/main

    • Merges changes from the remote’s main branch into the current branch
    • Typically used after git fetch to integrate upstream changes

    Example workflow:

    git checkout feature/my-feature
    git fetch origin
    git merge origin/main
    # Resolve any merge conflicts if they occur
  3. git rebase -i HEAD~3

    • Interactively rebases the last 3 commits for a clean history
    • Useful for squashing commits or reordering them before pushing
    • Opens an editor where you can mark commits to squash, reword, edit, etc.

    Example:

    git rebase -i HEAD~3
    # In the opened editor, change 'pick' to 'squash' for commits you want to combine
  4. git cherry-pick <commit-hash>

    • Applies a specific commit from another branch
    • Helpful when you want to bring in isolated changes without merging entire branches

    Example:

    git cherry-pick 5f83c68
    # This applies the changes from commit 5f83c68 to your current branch

History and Tracking

Understanding your project’s history is crucial for debugging and code review:

  1. git log --oneline --graph --decorate --all

    • Displays commit history as a compact, visual graph
    • Gives a clear overview of branch structure and merge history

    Example output:

    * 5f83c68 (HEAD -> main, origin/main) Merge pull request #123
    || * 2e3d4f5 Add new payment gateway
    | * 1a2b3c4 Update user model
    |/
    * 6789abc Refactor authentication system
  2. git diff HEAD^ HEAD

    • Shows the changes introduced by the last commit
    • Useful for quick code reviews or verifying recent changes

    Example:

    git diff HEAD^ HEAD
    # This will show the diff of the last commit
  3. git blame <file> -L 10,20

    • Shows who last changed lines 10 to 20 in a file
    • Helpful for understanding code evolution and identifying experts for specific code sections

    Example:

    git blame app/models/user.rb -L 10,20
    # This will show who last modified lines 10-20 in the user.rb file

Managing Tags

Tags are essential for marking release points:

  1. git tag -a v1.0.0 -m "Version 1.0 release"

    • Creates an annotated tag for release with a description
    • Use semantic versioning for clear communication of release significance

    Example:

    git tag -a v1.0.0 -m "First stable release with core features implemented"
  2. git push origin v1.0.0

    • Pushes the tag to the remote repository
    • Ensures release points are shared with the team and available for deployment

    Example:

    git push origin v1.0.0
    # To push all tags at once: git push origin --tags

Handling Remotes

Managing connections to remote repositories is important for collaboration:

  1. git remote -v

    • Lists all configured remote repositories with their URLs
    • Useful for verifying your remote connections

    Example output:

    origin  https://github.com/username/repo.git (fetch)
    origin  https://github.com/username/repo.git (push)
    upstream  https://github.com/original-owner/repo.git (fetch)
    upstream  https://github.com/original-owner/repo.git (push)
  2. git fetch --all --prune

    • Fetches changes from all remotes and prunes stale branches
    • Keeps your local repository up-to-date with all remote changes

    Example:

    git fetch --all --prune
    # This will update all remote branches and remove any that no longer exist on the remote

Undoing Changes

Sometimes you need to revert changes or go back to a previous state:

  1. git reset --soft HEAD~1

    • Reverts the last commit but keeps changes staged for re-commit
    • Useful when you want to restructure your last commit

    Example:

    git reset --soft HEAD~1
    # Make changes, stage them
    git commit -m "Restructured previous commit with additional changes"
  2. git checkout -- <file>

    • Reverts local changes in a file to the last commit
    • Helpful for discarding unwanted modifications in a specific file

    Example:

    git checkout -- app/models/user.rb
    # This will discard all uncommitted changes in user.rb

Advanced Git Techniques

To truly use Git like a pro, consider these advanced techniques:

  1. Git Hooks

    • Scripts that run automatically on certain Git events
    • Useful for enforcing coding standards, running tests, etc.
    • Example: Create a pre-commit hook to run linters
  2. Git Submodules

    • Allow you to keep a Git repository as a subdirectory of another Git repository
    • Useful for including external libraries or shared components
    • Example: git submodule add https://github.com/example/library.git external/library
  3. Git Worktrees

    • Manage multiple working trees attached to the same repository
    • Useful for working on different branches simultaneously without switching
    • Example: git worktree add -b hotfix/critical-bug ../hotfix main
  4. Git Bisect

    • Binary search through your commit history to find the commit that introduced a bug
    • Example:
      git bisect start
      git bisect bad  # Current version is bad
      git bisect good v1.0.0  # Last known good version
      # Git will checkout commits for you to test
      git bisect good  # or git bisect bad, depending on your test
      # Repeat until the culprit is found
      git bisect reset  # to end the bisect session

By mastering these Git commands and techniques, you’ll be able to manage your projects more efficiently, collaborate more effectively with your team, and maintain a clean, organized codebase. Remember, practice is key to becoming proficient with Git, so don’t hesitate to experiment in a safe environment as you learn.

Pro tip: Consider setting up aliases for commonly used Git commands to speed up your workflow. For example, you could add these to your .gitconfig file:

[alias]
    co = checkout
    br = branch
    ci = commit
    st = status
    unstage = reset HEAD --
    last = log -1 HEAD
    visual = !gitk

This allows you to use shortcuts like git co instead of git checkout, saving time and keystrokes.

Remember, Git is a powerful tool, and with great power comes great responsibility. Always think before you push, especially when working with shared branches. Happy coding!

Cheers,

Sim