Skip to content
Git Advanced Topics — Complete Power User Guide

Git Advanced Topics — Complete Power User Guide

DodaTech Updated Jun 6, 2026 10 min read

Once you’ve mastered the basic Git workflow, advanced features like tags, hooks, submodules, and signed commits help you automate workflows, manage large files, collaborate on complex projects, and integrate with CI/CD pipelines.

What You’ll Learn

By the end of this tutorial, you’ll tag releases with semantic versioning, set up Git hooks for automated linting and testing, embed submodules for shared libraries, manage large files with Git LFS, sign commits cryptographically, and configure CI/CD pipelines.

Why Advanced Git Matters

Professional development requires more than commit-push-pull. Tags ensure reproducible releases. Hooks prevent bad code from ever being committed. Submodules let you share libraries across projects. Signed commits prove authenticity — critical for open source and enterprise security. DodaTech uses all of these: signed releases for Durga Antivirus Pro, hooks for Doda Browser builds, and submodules for shared components across products.

Advanced Git Learning Path

    flowchart LR
  A[Git Basics] --> B[Branching & Merging]
  B --> C[Remote Repositories]
  C --> D[Undoing Changes]
  D --> E[Advanced Git]
  style E fill:#3b82f6,stroke:#fff,color:#fff
  
Prerequisites: Solid Git fundamentals — commits, branches, merges, remotes. Review Git and Git first.

Advanced Topics Overview

    flowchart TD
  A[Git Advanced] --> B[Tagging<br/>Releases & versions]
  A --> C[Hooks<br/>Run scripts on events]
  A --> D[Submodules<br/>Nested repos]
  A --> E[LFS<br/>Large files]
  A --> F[Signing<br/>Verify identity]
  A --> G[CI/CD<br/>Automated pipelines]
  

Tagging — Marking Releases

Tags label specific commits as important — typically for releases. Think of them like sticky notes on a timeline: “v1.0.0 — shipped to production here.”

# Lightweight tag (just a pointer — no metadata)
git tag v1.0.0

# Annotated tag (recommended — includes message, author, date)
git tag -a v1.0.0 -m "Release version 1.0.0"

# Tag a specific commit (not just HEAD)
git tag -a v0.9.0 a1b2c3d -m "Beta release"

# List tags
git tag
git tag -l "v1.*"           # filter by pattern

# View tag details
git show v1.0.0

# Push tags to remote
git push origin v1.0.0      # single tag
git push --tags              # all tags

# Delete tags
git tag -d v1.0.0           # local
git push origin --delete v1.0.0  # remote

# Checkout a tag (detached HEAD)
git checkout v1.0.0

# Create a branch from a tag
git checkout -b release-v1 v1.0.0

Why annotated tags? Lightweight tags are just pointers — like a bookmark with no label. Annotated tags store the tagger name, date, message, and can be GPG-signed. For releases, always use annotated tags.

Semantic Versioning

v1.2.3
| | └── Patch (bug fixes, backward compatible)
| └──── Minor (new features, backward compatible)
└────── Major (breaking changes)

Analogy: Think of version numbers like hotel room numbers: v1 is the floor (major), v2 is the room (minor), v3 is the bed (patch). Breaking changes = moving to a new floor.

Git Hooks — Automation Triggers

Hooks are scripts that run automatically on Git events. They live in .git/hooks/:

ls .git/hooks/
# applypatch-msg.sample  pre-applypatch.sample  pre-commit.sample
# commit-msg.sample      pre-push.sample        prepare-commit-msg.sample

Common Hooks

HookWhen it runsUse case
pre-commitBefore commit messageLint code, run tests
prepare-commit-msgBefore commit editor opensAuto-fill message
commit-msgAfter message is writtenValidate message format
pre-pushBefore pushingRun full test suite
post-mergeAfter mergeInstall dependencies

Example: Pre-commit Hook

#!/bin/sh
# .git/hooks/pre-commit — make executable with chmod +x

# Prevent committing to main or develop
branch=$(git rev-parse --abbrev-ref HEAD)
if [ "$branch" = "main" ] || [ "$branch" = "develop" ]; then
    echo "Direct commits to $branch are not allowed"
    exit 1
fi

# Check for debug statements
if git diff --cached | grep -q "debugger\|console.log"; then
    echo "Remove debug statements before committing"
    exit 1
fi

Line-by-line explanation:

  • #!/bin/sh — tells the system to run this as a shell script
  • git rev-parse --abbrev-ref HEAD — gets the current branch name
  • git diff --cached — checks the staged changes (what’s about to be committed)
  • exit 1 — aborts the commit with an error
Hooks in .git/hooks/ are not tracked by Git. To share hooks with your team, store them in a .githooks/ directory and run git config core.hooksPath .githooks.

Git Submodules — Repos Within Repos

Submodules embed one Git repo inside another at a specific commit:

# Add a submodule
git submodule add https://github.com/user/shared-lib.git libs/shared

# Clone a repo WITH its submodules
git clone --recurse-submodules https://github.com/user/project.git

# Or after cloning without --recurse-submodules:
git submodule init
git submodule update

# Update all submodules to latest
git submodule update --remote

Analogy: Submodules are like a bookshelf in your house. The shelf holds separate books (repos). Your house repo knows which books are on the shelf and which editions, but the books have their own authors and history.

Typical Submodule Workflow

# When pulling changes that include a submodule update:
git pull
git submodule update --init --recursive

# When you update a submodule:
cd libs/shared
git checkout main
git pull
cd ../..
git add libs/shared
git commit -m "Update shared-lib to latest"

Git LFS — Large File Storage

Git stores every version of every file. Binary files (images, videos, ZIPs) bloat the repository quickly. LFS replaces them with text pointers:

# Install LFS
git lfs install

# Track file types
git lfs track "*.psd"
git lfs track "*.mp4"
git lfs track "*.zip"

# The configuration is stored in .gitattributes
cat .gitattributes
# *.psd filter=lfs diff=lfs merge=lfs -text

# Add and commit as normal
git add .gitattributes
git add large-file.psd
git commit -m "Add large file with LFS"

# List tracked patterns
git lfs track

# Pull LFS files
git lfs pull

What happens behind the scenes: Your repo stores a small pointer file (like “file.psd is actually at URL X”). The real binary goes to LFS storage on GitHub. When you clone, you get pointers instantly; the actual files download on demand.

Signing Commits & Tags

Cryptographically sign your commits to prove they came from you:

# Generate GPG key
gpg --full-generate-key

# List keys
gpg --list-secret-keys --keyid-format LONG

# Configure Git to use it
git config --global user.signingkey KEY_ID
git config --global commit.gpgSign true    # sign all commits

# Sign a specific commit
git commit -S -m "Signed commit"

# Sign a tag
git tag -s v1.0.0 -m "Signed release"

# Verify
git verify-commit HEAD
git verify-tag v1.0.0

# Add public key to GitHub
gpg --armor --export KEY_ID

Why sign commits? Anyone can commit with your name and email. A GPG signature cryptographically proves the commit came from you. Open source projects like Linux kernel require signed commits.

Git Configuration & Aliases

Create shortcuts for common commands:

# Useful aliases
git config --global alias.lg "log --oneline --graph --all --decorate"
git config --global alias.ci "commit"
git config --global alias.co "checkout"
git config --global alias.br "branch"
git config --global alias.st "status -sb"
git config --global alias.unstage "reset HEAD --"
git config --global alias.last "log -1 HEAD"

# Usage
git lg              # pretty log graph
git unstage file    # unstage a file
git last            # show last commit

Useful Config Options

# Case-insensitive filenames
git config --global core.ignorecase true

# Auto-convert line endings
git config --global core.autocrlf input       # Linux/macOS
git config --global core.autocrlf true        # Windows

# Colored output
git config --global color.ui auto

# Rebase when pulling (instead of merge)
git config --global pull.rebase true

CI/CD with Git

Continuous Integration / Continuous Deployment automates testing and deployment:

# .github/workflows/ci.yml — GitHub Actions example
name: CI
on: [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npm ci
      - run: npm test
      - run: npm run build

How it works: Every time you push, GitHub Actions checks out your repo, sets up Node.js, installs dependencies, and runs tests. If tests fail, the PR is blocked.

Git Bundle — Offline Transfer

Bundle your repo as a single file for air-gapped environments:

# Create a bundle
git bundle create repo.bundle --all

# Restore from bundle
git clone repo.bundle my-repo

# Create incremental bundle (last 5 commits)
git bundle create incremental.bundle HEAD~5..HEAD

Common Mistakes

1. Committing large files without LFS

Binary files like images and videos bloat the repository forever. Even if deleted later, they remain in history. Use git lfs track before adding them.

2. Not sharing hooks with the team

Hooks in .git/hooks/ are local and not committed. Store hooks in a version-controlled .githooks/ directory and configure core.hooksPath for team-wide enforcement.

3. Forgetting --recurse-submodules when cloning

Without this flag, submodule directories are empty. Run git submodule update --init --recursive after cloning to populate them.

4. Tagging without pushing

Tags are local until explicitly pushed. git push does not push tags by default. Use git push --tags or git push origin v1.0.0.

5. Overriding hooks with --no-verify

git commit --no-verify skips pre-commit hooks. While useful for emergencies, habitual use defeats the purpose.

6. Using git config --global when project config is needed

--global applies to all repos. Use project-level config (without --global) for settings that should differ per project — like user.email for work vs personal.

Practice Questions

1. What is the difference between lightweight and annotated tags?

Answer: Lightweight tags are just pointers to a commit — no metadata. Annotated tags store the tagger name, date, message, and can be GPG-signed. Always use annotated tags for releases.

2. How do you share Git hooks with your team?

Answer: Store hooks in a .githooks/ directory (tracked by Git) and run git config core.hooksPath .githooks so team members automatically get the hooks.

3. What is Git LFS and when should you use it?

Answer: Large File Storage replaces binary files with text pointers. Use it for files > 1MB that change often: images, audio, video, design assets.

4. Why should you sign your commits?

Answer: Signing cryptographically proves the commit came from you, preventing impersonation. It’s required by many open source projects and enterprise security policies.

Challenge

Set up a pre-commit hook that checks for TODO comments and prevents committing them. Share the hook with your team via a .githooks directory.

FAQ

What is the difference between lightweight and annotated tags?
Lightweight tags are just pointers to a commit. Annotated tags store the tagger name, date, and message, and are GPG-signable. Always use annotated tags for releases.
How do I share Git hooks with my team?
Store hooks in a .githooks/ directory (tracked by Git) and run git config core.hooksPath .githooks.
What is Git LFS and when should I use it?
Large File Storage replaces binary files with text pointers. Use it for files > 1MB that change often — images, audio, video, design assets.
What is a submodule?
A Git submodule is a reference to another Git repository embedded inside your repo at a specific commit. It’s like a pointer to an external project.
How do I set up CI/CD for my Git repo?
Push to GitHub/GitLab and add a workflow file (.github/workflows/ for GitHub Actions, .gitlab-ci.yml for GitLab).
How do I remove a submodule?
git submodule deinit -f libs/shared, remove the directory, then git rm -f libs/shared and commit.

Try It Yourself

# 1. Create a hooks directory
mkdir .githooks

# 2. Create a pre-commit hook
cat > .githooks/pre-commit << 'EOF'
#!/bin/sh
echo "Running pre-commit checks..."
# Check for merge markers
if git diff --cached | grep -E "^\+<<<<<<<|^\+=======|^\+>>>>>>>"; then
    echo "Merge conflict markers found!"
    exit 1
fi
echo "Checks passed"
EOF
chmod +x .githooks/pre-commit

# 3. Configure Git to use it
git config core.hooksPath .githooks
git add .githooks/pre-commit
git commit -m "Add pre-commit hook for merge conflict detection"

# 4. Set up useful aliases
git config alias.lg "log --oneline --graph --all --decorate"
git config alias.unstage "reset HEAD --"

# 5. Create an annotated tag
git tag -a v0.1.0 -m "First development release"
git log --oneline

What’s Next

Keep this reference handy:

TopicDescription
https://tutorials.dodatech.com/devops/git/git-reference/Complete Git command cheatsheet
https://tutorials.dodatech.com/devops/cloud/aws/reference/AWS services reference

Related topics to explore:

What’s Next

Congratulations on completing this Git Advanced tutorial! Here’s where to go from here:

  • Practice daily — Consistency is more important than long study sessions
  • Build a project — Apply what you learned by building something real
  • Explore related topics — Check out other tutorials in the same category
  • Join the community — Discuss with other learners and share your progress

Remember: every expert was once a beginner. Keep coding!

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro