Back to Blog Posts

Git Advanced Workflows: Mastering Collaboration and Code Management

11 min read
GitDevOpsVersion ControlCollaboration

Introduction

While basic Git commands get you started, mastering advanced Git workflows is essential for efficient team collaboration and code management. This guide covers branching strategies, advanced techniques, and best practices for managing complex projects.

Understanding Git Internals

The Three Trees

Git manages your project through three main trees:

# Working Directory - your actual files
# Staging Area (Index) - proposed next commit
# Repository (HEAD) - last commit

# View the three trees
git status                    # Working directory vs staging
git diff                      # Working directory vs staging
git diff --staged             # Staging vs HEAD
git diff HEAD                 # Working directory vs HEAD

Git Objects

# Git stores data as objects
# Blob - file content
# Tree - directory structure
# Commit - snapshot with metadata
# Tag - named reference to a commit

# Examine Git objects
git cat-file -p HEAD          # Show commit object
git cat-file -p HEAD^{tree}   # Show tree object
git ls-tree HEAD              # List tree contents

# View object details
git show --pretty=raw HEAD    # Raw commit data

Advanced Branching Strategies

Git Flow

A robust branching model for projects with scheduled releases:

# Initialize Git Flow
git flow init

# Feature branches
git flow feature start new-feature    # Create from develop
git flow feature finish new-feature   # Merge back to develop

# Release branches
git flow release start 1.0.0         # Create from develop
git flow release finish 1.0.0        # Merge to main and develop

# Hotfix branches
git flow hotfix start critical-fix   # Create from main
git flow hotfix finish critical-fix  # Merge to main and develop

# Manual Git Flow (without extensions)
# Feature branch
git checkout -b feature/new-feature develop
# ... work on feature ...
git checkout develop
git merge --no-ff feature/new-feature
git branch -d feature/new-feature

# Release branch
git checkout -b release/1.0.0 develop
# ... prepare release ...
git checkout main
git merge --no-ff release/1.0.0
git tag -a v1.0.0 -m "Release version 1.0.0"
git checkout develop
git merge --no-ff release/1.0.0

GitHub Flow

A simpler workflow ideal for continuous deployment:

# 1. Create feature branch from main
git checkout -b feature/add-user-auth main

# 2. Make changes and commit
git add .
git commit -m "feat: implement user authentication"

# 3. Push to remote
git push -u origin feature/add-user-auth

# 4. Create Pull Request (on GitHub)
# 5. Review and merge
# 6. Deploy from main

# Clean up after merge
git checkout main
git pull origin main
git branch -d feature/add-user-auth
git push origin --delete feature/add-user-auth

GitLab Flow

Combines feature branches with environment branches:

# Feature development
git checkout -b feature/new-dashboard main
# ... develop feature ...
git push -u origin feature/new-dashboard

# Merge to main (after review)
git checkout main
git merge --no-ff feature/new-dashboard

# Deploy to staging
git checkout staging
git merge --no-ff main

# Deploy to production
git checkout production
git merge --no-ff staging
git tag -a v2.1.0 -m "Release 2.1.0"

Interactive Rebase Mastery

Rewriting History

# Interactive rebase for last 5 commits
git rebase -i HEAD~5

# Commands in interactive rebase:
# pick   - use commit
# reword - change commit message
# edit   - stop for amending
# squash - meld into previous commit
# fixup  - like squash but discard message
# drop   - remove commit

# Example: Clean up feature branch before merging
git rebase -i main

# In the editor:
pick abc1234 Add user model
squash def5678 Fix typo in user model
pick ghi9012 Add user controller
reword jkl3456 Add tests
drop mno7890 Debug commit

# Advanced: Rebase onto different branch
git rebase --onto main feature-old feature-new

Splitting Commits

# Split a commit during interactive rebase
git rebase -i HEAD~3

# Mark commit as 'edit'
# When rebase stops:
git reset HEAD~
git add -p            # Add changes interactively
git commit -m "Part 1: Add user model"
git add .
git commit -m "Part 2: Add user validation"
git rebase --continue

Advanced Merging Techniques

Merge Strategies

# Different merge strategies
git merge -s recursive feature    # Default
git merge -s ours feature        # Keep our version
git merge -s theirs feature      # Does not exist! Use recursive -X

# Strategy options
git merge -X ours feature        # Favor our changes in conflicts
git merge -X theirs feature      # Favor their changes
git merge -X patience feature    # Better for diverged branches
git merge -X ignore-space-change feature

# Octopus merge (multiple branches)
git merge feature-1 feature-2 feature-3

Cherry-Picking

# Apply specific commits
git cherry-pick abc1234          # Single commit
git cherry-pick abc1234..def5678 # Range of commits
git cherry-pick -n abc1234       # No commit, just apply changes

# Cherry-pick with options
git cherry-pick -x abc1234       # Add source commit reference
git cherry-pick -e abc1234       # Edit commit message
git cherry-pick --strategy=recursive -X theirs abc1234

# Resolve cherry-pick conflicts
git cherry-pick abc1234
# ... resolve conflicts ...
git add .
git cherry-pick --continue
# Or abort: git cherry-pick --abort

Stash Advanced Usage

# Stash with message
git stash save "WIP: implementing user dashboard"

# Stash untracked files too
git stash -u
git stash --include-untracked

# Stash only staged changes
git stash --staged

# Create branch from stash
git stash branch new-feature stash@{0}

# Apply specific file from stash
git checkout stash@{0} -- path/to/file

# Interactive stash
git stash -p    # Choose hunks to stash

# List stashes with details
git stash list --oneline --decorate

# Show stash contents
git stash show -p stash@{1}

Searching and Debugging

Git Bisect

Find the commit that introduced a bug:

# Start bisect
git bisect start
git bisect bad                   # Current commit is bad
git bisect good v1.0.0          # Last known good commit

# Git checks out middle commit
# Test and mark as good or bad
git bisect good  # or git bisect bad

# Continue until bug is found
# Git will identify the first bad commit

# Automated bisect with script
git bisect start HEAD v1.0.0
git bisect run npm test

# End bisect
git bisect reset

Git Grep

Search through your codebase:

# Search in working directory
git grep "TODO"
git grep -n "function"          # Show line numbers
git grep -c "import"            # Count matches
git grep -i "error"             # Case insensitive

# Search in specific commit
git grep "API_KEY" HEAD~10

# Search with context
git grep -B 3 -A 3 "bug"        # 3 lines before/after

# Search only in certain files
git grep "class" -- "*.py"

# Complex searches
git grep -e "TODO" --and -e "FIXME"
git grep -e "error" --or -e "exception"

Git Log Advanced

# Formatting log output
git log --pretty=format:"%h - %an, %ar : %s"
git log --pretty=oneline --abbrev-commit
git log --graph --all --decorate

# Filter logs
git log --author="John"
git log --since="2023-01-01" --until="2023-12-31"
git log --grep="fix"            # Search commit messages
git log -S "function"           # Search for code changes
git log -G "regex.*pattern"     # Regex search

# File history
git log --follow -- path/to/file  # Follow renames
git log -p -- path/to/file        # Show patches
git log --all -- deleted_file     # Find deleted files

# Branch comparison
git log main..feature             # Commits in feature not in main
git log feature --not main        # Same as above
git log main...feature            # Diverged commits

Working with Remotes

Multiple Remotes

# Add multiple remotes
git remote add upstream https://github.com/original/repo.git
git remote add backup https://gitlab.com/backup/repo.git

# Fetch from all remotes
git fetch --all

# Push to multiple remotes
git remote set-url --add origin https://gitlab.com/backup/repo.git
git push origin main    # Pushes to both

# Different push/fetch URLs
git remote set-url --push origin https://github.com/fork/repo.git

# Prune stale remote branches
git remote prune origin
git fetch --prune       # Prune during fetch

Advanced Push/Pull

# Push with options
git push -u origin feature       # Set upstream
git push origin HEAD             # Push current branch
git push origin :old-branch      # Delete remote branch
git push --force-with-lease      # Safer force push

# Push tags
git push origin --tags           # All tags
git push origin v1.0.0          # Specific tag

# Pull with rebase
git pull --rebase origin main

# Pull specific branch
git pull origin feature:feature

# Fetch and merge manually
git fetch origin
git merge origin/main --ff-only

Submodules and Subtrees

Git Submodules

# Add submodule
git submodule add https://github.com/lib/library.git libs/library
git commit -m "Add library submodule"

# Clone with submodules
git clone --recursive https://github.com/project/repo.git
# Or after cloning:
git submodule update --init --recursive

# Update submodules
git submodule update --remote --merge
git submodule foreach git pull origin main

# Remove submodule
git submodule deinit -f libs/library
git rm -f libs/library
rm -rf .git/modules/libs/library

Git Subtrees

# Add subtree
git subtree add --prefix=libs/library \
  https://github.com/lib/library.git main --squash

# Pull updates
git subtree pull --prefix=libs/library \
  https://github.com/lib/library.git main --squash

# Push changes back
git subtree push --prefix=libs/library \
  https://github.com/lib/library.git feature-branch

Git Hooks

Client-Side Hooks

# pre-commit hook example
#!/bin/bash
# .git/hooks/pre-commit

# Run linter
npm run lint
if [ $? -ne 0 ]; then
  echo "Linting failed. Please fix errors before committing."
  exit 1
fi

# Check for sensitive data
if git diff --cached | grep -E "(API_KEY|SECRET|PASSWORD)" > /dev/null; then
  echo "Possible sensitive data detected. Please review your changes."
  exit 1
fi

# Make hook executable
chmod +x .git/hooks/pre-commit

Commit Message Hook

#!/bin/bash
# .git/hooks/commit-msg

# Check commit message format
commit_regex='^(feat|fix|docs|style|refactor|test|chore)(\(.+\))?: .{1,50}'
if ! grep -qE "$commit_regex" "$1"; then
  echo "Invalid commit message format!"
  echo "Format: type(scope): subject"
  echo "Example: feat(auth): add login functionality"
  exit 1
fi

Git Worktrees

Work on multiple branches simultaneously:

# Add new worktree
git worktree add ../project-hotfix hotfix-branch
git worktree add -b new-feature ../project-feature main

# List worktrees
git worktree list

# Remove worktree
git worktree remove ../project-hotfix

# Clean up stale worktrees
git worktree prune

Advanced Configuration

Git Aliases

# Useful aliases
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.ci commit
git config --global alias.st status

# Complex aliases
git config --global alias.lg "log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"

git config --global alias.unstage "reset HEAD --"
git config --global alias.last "log -1 HEAD"
git config --global alias.visual "!gitk"

# Alias with shell function
git config --global alias.feature '!f() { git checkout -b feature/$1 main; }; f'

Git Attributes

# .gitattributes file
# Set diff driver for specific files
*.json diff=json
*.md diff=markdown

# Configure line endings
* text=auto
*.sh text eol=lf
*.bat text eol=crlf

# Mark files as binary
*.jpg binary
*.pdf binary

# Exclude from archive
.gitignore export-ignore
.gitattributes export-ignore
tests/ export-ignore

Recovering Lost Work

Reflog

# View reflog
git reflog
git reflog show feature-branch

# Recover deleted branch
git reflog | grep "branch: Created"
git checkout -b recovered-branch abc1234

# Recover lost commits
git reflog
git cherry-pick abc1234

# Reset to previous state
git reset --hard HEAD@{5}

# Clean reflog
git reflog expire --expire=30.days --all
git gc --prune=now

Finding Lost Objects

# Find dangling commits
git fsck --lost-found

# Examine dangling objects
git show <dangling-commit-sha>

# Recover dangling blob
git cat-file -p <blob-sha> > recovered-file.txt

# Find large objects in history
git rev-list --objects --all | \
  git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' | \
  sort -k3 -n | tail -10

Performance Optimization

# Optimize repository
git gc --aggressive --prune=now

# Partial clone (large repos)
git clone --filter=blob:limit=1m https://github.com/large/repo.git

# Shallow clone
git clone --depth=1 https://github.com/project/repo.git

# Enable filesystem monitor
git config core.fsmonitor true

# Pack refs
git pack-refs --all

# Verify repository integrity
git fsck --full

Best Practices

Commit Guidelines

# Good commit messages
git commit -m "feat(auth): implement JWT authentication

- Add JWT token generation
- Implement token validation middleware
- Add refresh token functionality

Closes #123"

# Semantic commit format
# type(scope): subject
#
# body
#
# footer

# Types: feat, fix, docs, style, refactor, test, chore

Branch Naming

# Consistent branch naming
feature/user-authentication
bugfix/login-error-handling
hotfix/security-vulnerability
release/v2.0.0
chore/update-dependencies

Conclusion

Mastering advanced Git workflows enables efficient collaboration, cleaner project history, and better code management. Key takeaways:

  1. Choose the right branching strategy for your team and project
  2. Use interactive rebase to maintain clean history
  3. Leverage Git's search capabilities for debugging
  4. Automate with hooks to enforce standards
  5. Configure Git to match your workflow
  6. Always have a recovery plan - Git rarely loses data permanently

Remember: with great power comes great responsibility. Use force push and history rewriting carefully, especially on shared branches.

Happy collaborating!