

- Description
- Vue aérienne d’une petite rivière qui se jette dans un grand fleuve entre des rives boisées.
- Source
- Wikideas1 · original page
- Licence
- CC0 Public domain
Git in Depth
Understand Git's object model (blob, tree, commit, tag), the index, references, and reflog: that's what makes commands predictable.
In brief
- Understand Git's object model (blob, tree, commit, tag), the index, references, and reflog: that's what makes commands predictable.
- Choose between
merge,rebase,cherry-pick,revert,resetby understanding their effects on history. - Find a defect with
bisect, recover “lost” work withreflog. - Manage large files and large repositories (LFS, partial/shallow clone, sparse-checkout), secrets, and signing.
- Know where Git on Azure DevOps, GitHub, and GitLab differs, and what AI brings (and does not replace).
On this page
- The basics in ten commands
- The object model
- The index and the three areas
- References, HEAD, and reflog
- Merging with merge, rebase, cherry-pick, revert
- Undo with reset, restore, revert
- Find with log, blame, bisect, pickaxe
- Hooks
- Submodules, subtrees, LFS
- Large repositories, partial, shallow, sparse, monorepo
- Signing commits and tags
- Secret management
- Expert commands
- Classic pitfalls
- Git on Azure DevOps, GitHub, and GitLab
- AI contribution
- See also
Level: 🟢→🔴
The basics in ten commands#
🟢 Novice level
git clone <url> # copy a repository (full history)
git status # where am I?
git switch -c feature/kpi # create and switch to a branch
git add chemin/fichier.sql # stage (index) a change
git commit -m "Message" # save locally
git fetch # retrieve remote WITHOUT touching my work
git pull --rebase # fetch + replay my commits on top
git push -u origin feature/kpi # publish the branch
git log --oneline --graph --decorate # read history
git diff / git diff --staged # see what changed (unstaged / staged)
Beginner's golden rule: git status before anything else, git fetch rather than git pull when in doubt, never rewrite a shared branch.
Configure once: git config --global user.name / user.email, core.autocrlf (see 06), pull.rebase true or pull.ff only, init.defaultBranch main, rerere.enabled true, fetch.prune true.
The object model#
🟡 Practical level
Everything is stored in .git/objects, by hash identifier (principle). Four object types:
| Object | Contains | Example |
|---|---|---|
| blob | The contents of a file (without its name) | a .sql script |
| tree | A (mode, name, identifier) list of blobs/trees |
a folder |
| commit | A root tree + parents + author + message | a snapshot |
| annotated tag | A named pointer + signer + message | v9.1.0 |
commit C3 ──► root tree ──► blob README.md
│ └──► tree Datamart/ ──► blob 010_Core.sql
└─parent─► commit C2 ──► ...
Check for yourself:
git cat-file -t HEAD # commit
git cat-file -p HEAD # displays tree, parent, author, message
git cat-file -p HEAD^{tree} # contents of root folder
git ls-tree -r HEAD --name-only | head
A branch is just a 41-byte file containing a commit identifier (.git/refs/heads/main). So creating a branch is instant and free. A commit knows its parents, never its children: history is a directed acyclic graph (DAG).
Physical storage: “loose” objects (zlib), then grouped into packfiles with delta compression (git gc). A repository may seem large in history but compact after gc.
A renamed file is not stored as renamed: Git compares trees and detects similarity (git log --follow, git diff -M). If you move and heavily rewrite a file in the same commit, detection fails: make two commits.
The index and the three areas#
🟡 Practical level
Git manipulates three areas:
Working directory --git add--> Index (staging area) --git commit--> Repository
▲ │ │
└────────── git restore ◄───────────┘ git restore --staged ◄─────────┘
The index is a list of what the next commit will be. Benefit: compose clean commits. git add -p stages by chunks (hunks); git restore --staged unstages; git stash sets aside the working state (equivalent to a lightweight shelveset, see 03).
git add -p script.sql # choose hunk by hunk
git commit -v # view diff in message editor
git commit --fixup <sha> # prepare a fix to integrate
git rebase -i --autosquash origin/main
Symptom: “my modified file isn't in the commit.” Cause: change made after git add. The index keeps the old state. Fix: git status shows both lists (“to be committed” / “not staged”); run git add again.
References, HEAD, and reflog#
🟡 Practical level
- References:
refs/heads/*(local branches),refs/remotes/origin/*(remote tracking),refs/tags/*. - HEAD: the current position; normally a symbolic reference to a branch. Detached HEAD: HEAD points directly to a commit (after
git checkout <sha>or a tag). Commits created then are attached to no branch: create one (git switch -c rescue) before leaving. - Relative notation:
HEAD~2(two “first” parents back),HEAD^2(second parent of a merge),main@{yesterday},:/text(commit whose message contains “text”).
Reflog is the local journal of HEAD and branch movements. It saves you: a failed reset --hard or rebase doesn't destroy commits, they simply become unreachable by a branch and remain recoverable for about 90 days by default (30 days for unreachable ones, orders of magnitude for default gc.reflogExpire configuration).
git reflog # HEAD journal
git reset --hard HEAD@{3} # return to state from three moves ago
git branch rescue <sha> # reattach a recovered commit
git fsck --lost-found # orphan objects
After a git rebase -i where a line was deleted by mistake, a SQL migration script seems lost. git reflog shows the pre-rebase state (HEAD@{5}: rebase (start)); git reset --hard HEAD@{6} restores it. Reflog is local: it does not protect a deleted clone.
Merging with merge, rebase, cherry-pick, revert#
🟡 Practical level
| Operation | Effect on history | When to use it | Risk |
|---|---|---|---|
merge (fast-forward) |
Simply moves the pointer forward | Short branch with no divergence | None |
merge --no-ff |
Creates a two-parent merge commit | Keep the trace of a feature | Denser history |
merge --squash |
A single commit without merge link | Integrate a “draft” branch | Loss of detail; branch relationships not tracked |
rebase |
Replays your commits on a new base (new identifiers) | Clean up a private branch before PR | Rewrite: forbidden on shared branch |
cherry-pick |
Copies a commit elsewhere (new identifier) | Backport a fix to a release | Duplicates, repeated conflicts |
revert |
Creates a reverse commit | Undo on public branch | Reverted merge: must be handled carefully |
Three-way merge: Git finds the common ancestor, compares each branch to it, and combines. Default strategy ort (replacement for recursive, verify by version). A conflict occurs when both sides modify the same area.
git merge feature/kpi
# conflict:
git status # "both modified"
git diff --name-only --diff-filter=U
# edit markers <<<<<<< ======= >>>>>>>
git add fichier && git merge --continue
git merge --abort # give up
git config --global merge.conflictStyle zdiff3 # also shows common base
git config --global rerere.enabled true # remembers resolutions
Cherry-pick a fix to a release branch:
git switch release/9.1
git cherry-pick -x abc1234 # -x adds "(cherry picked from ...)": traceability
Use -x for every cherry-pick to a release: audit can find the origin. Even better: fix first on the oldest affected branch, then merge forward (see 05), so the VCS knows the fix is present everywhere.
Rebasing an already pushed branch then push --force: teammates who pulled the old version end up with duplicate commits or phantom conflicts. Fix: git push --force-with-lease (refuses if remote advanced), only on your branch; team rule: never force-push on main or on a release branch (branch protections).
git rebase --onto new_base old_base my_branch transplants a branch that started from the wrong point. git rebase --rebase-merges preserves merge topology. git merge-base A B gives the common ancestor; git range-diff compares two versions of a commit series before/after rebase.
Undo with reset, restore, revert#
🟡 Practical level
| Need | Command | Note |
|---|---|---|
| Undo the last commit while keeping changes | git reset --soft HEAD~1 |
Index and files untouched |
| Undo while keeping files, clear the index | git reset --mixed HEAD~1 |
Default |
| Throw everything away (⚠️ destructive) | git reset --hard HEAD~1 |
Recoverable via reflog if committed |
| Restore a file from index/HEAD | git restore fichier |
Loses uncommitted local changes |
| Restore from a commit | git restore --source=<sha> fichier |
|
| Undo a published commit | git revert <sha> |
History preserved |
| Fix the last message | git commit --amend |
Commit not pushed yet |
| Delete untracked files | git clean -fdn then -fd |
-n = simulation |
git reset --hard and git clean -fd destroy uncommitted changes with no safety net at all (reflog does not contain them). Habit: git stash -u before any destructive operation.
Find with log, blame, bisect, pickaxe#
🟡 Practical level
git log --oneline --graph --all --decorate -20
git log -- chemin/script.sql # history of a file
git log --follow -p -- chemin/script.sql # follow across renames
git log -S"DateExploitation" --oneline # pickaxe: commits that add/remove the string
git log -G"regex" -p # same by regular expression
git log --since="2026-09-01" --author="name" --stat
git blame -w -C -L 120,160 script.sql # -w ignores spaces, -C follows copies
git shortlog -sn --no-merges # contribution by author
git bisect automates binary search for the faulty commit: you mark a “bad” commit and a “good” one, Git proposes a midpoint, you test, and so on in O(log n).
git bisect start
git bisect bad HEAD
git bisect good v9.0.0
# Git checks out a commit: test then tell it
git bisect good # or git bisect bad
# or automate:
git bisect run powershell -File tests/Test-Datamart.ps1
git bisect reset
Between two kit versions: 480 commits. Bisect finds the culprit in at most 9 steps (2⁹ = 512). With an automated test (consolidating a sample then comparing three business totals), the operation runs without intervention. Condition: each commit must be testable in isolation (atomic commits, no “broken” commit).
Hooks#
🟡 Practical level
Hooks are scripts in .git/hooks (not versioned). To share them: git config core.hooksPath .githooks and version the folder, or use a manager (pre-commit, Husky, lefthook).
| Hook | When | Usage |
|---|---|---|
pre-commit |
Before commit | Syntax checks, UTF-8/BOM encoding, secret detection |
commit-msg |
Message entered | Enforce a format (ticket reference) |
pre-push |
Before push | Run quick tests |
post-merge, post-checkout |
After update | Regenerate artifacts |
pre-receive / update (server) |
On receive | Server-side policy (not available on all cloud hosts) |
#!/bin/sh
# .githooks/commit-msg: requires a ticket reference
grep -Eq '^(#[0-9]+|AB#[0-9]+) ' "$1" || { echo "Message: prefix with AB#<work item no.>"; exit 1; }
Local hooks can be bypassed (--no-verify): they are an aid, not a barrier. The barrier is server-side validation (branch policies, required pipeline).
Submodules, subtrees, LFS#
🟡 Practical level
Submodule (git submodule): a Git repository inside another, pinned to a specific commit. Useful for sharing a library (a shared SQL toolbox) across repositories. Cost: --recurse-submodules clone, explicit updates, frequent confusion.
Subtree (git subtree): copies a repository's history into a subfolder; no special command for consumers, but heavier history.
| Criterion | Submodule | Subtree | Copy / package |
|---|---|---|---|
| Simplicity for the consumer | 2/5 | 4/5 | 5/5 |
| Version traceability | 5/5 | 3/5 | 4/5 |
| Sending changes back to source | 3/5 | 3/5 | 1/5 |
Git LFS (Large File Storage) replaces large files with small pointers in history; content is stored elsewhere.
git lfs install
git lfs track "*.pbix" "*.bak" "*.zip"
git add .gitattributes
git lfs ls-files
git lfs migrate import --include="*.pbix" --everything # rewrites history!
LFS is not a cure-all: storage and bandwidth are billed or capped by the host; one .pbix modified 200 times = 200 stored versions. Prefer PBIP format for reports (06) and reserve LFS for true binaries.
Large repositories, partial, shallow, sparse, monorepo#
🔴 Expert level
| Technique | Command | Effect | Limit |
|---|---|---|---|
| Shallow clone | git clone --depth 1 |
Only the latest commit | No history: log, blame, bisect limited; merges difficult |
| Partial clone | git clone --filter=blob:none |
Full history, blobs on demand | Requires compatible server; later network access |
| Sparse checkout | git sparse-checkout set Datamart/ |
Materializes only certain folders | Repository stays complete (except partial) |
| Commit-graph, FSMonitor | git config core.fsmonitor true, git maintenance start |
Speed up status/log |
Depends on platform |
| Scalar | scalar clone <url> |
Ready-to-use setup for large repositories | Microsoft tool, integrated into Git |
git clone --filter=blob:none --sparse https://.../mon-produit.git
cd mon-produit
git sparse-checkout set Datamart/Datamart Reports
Monorepo or multi-repo? The monorepo (one repo, multiple products/modules) brings: cross-cutting atomic changes (one commit modifies SQL and report), a single history, one toolchain. Cost: size, non-granular permissions, builds to make incremental. Multi-repo brings: independent permissions and cycles; cost: version coordination, synchronization, “dependency hell.”
A datamart (SQL scripts) and its Power BI reports evolve together: renaming a column breaks reports. A monorepo allows a single commit/PR that updates the SQL layer and the semantic layer; two repos require a cross PR and a deployment order.
| Criterion | Monorepo | Multi-repo |
|---|---|---|
| Atomic cross-cutting changes | 5/5 | 2/5 |
| Permission isolation | 2/5 | 5/5 |
| Clone performance | 2/5 (3/5 with partial) | 5/5 |
| CI simplicity | 3/5 | 4/5 |
Context: team of 5 to 30 people, a few GB of content.
Signing commits and tags#
🔴 Expert level
The author of a commit is a simple string (user.name): it can be spoofed. Cryptographic signing proves the signer's identity.
git config --global gpg.format ssh # sign with SSH key (Git ≥ 2.34)
git config --global user.signingkey ~/.ssh/id_ed25519.pub
git config --global commit.gpgsign true
git config --global tag.gpgsign true
git log --show-signature -1
git tag -s v9.1.0 -m "Release 9.1.0"
git verify-tag v9.1.0
Choices: GPG (legacy, keyring management), SSH (simpler, already present), S/MIME (enterprise certificates). Platforms show a “verified” badge; for a real audit requirement, enforce signing through branch policy (platform-dependent, verify). Don't confuse: signature = identity; content integrity = hashing.
Sign at least release tags: they underpin release reproducibility.
Secret management#
🔴 Expert level
A committed secret (SQL password, connection string, token, API key) is compromised even after deletion: it remains in history, forks, and clones.
Defense hierarchy:
- Don't write it: use variables, a vault (Azure Key Vault), integrated auth/managed identity instead of passwords.
- Prevent commit:
.gitignore(*.local.*,*.pfx,appsettings.*.jsonfiles),pre-commithook with a detector (gitleaks, detect-secrets). - Detect server-side: platform secret scanning (GitHub secret scanning/push protection, GitHub Advanced Security for Azure DevOps, also offered as separate products Secret Protection and Code Security, limited to Azure DevOps Services — license to verify).
- If leaked: revoke and rotate immediately (rewriting history is never enough), then clean with
git filter-repo(recommended tool replacingfilter-branch) or BFG, then force-push rewritten history and have the team re-clone.
git filter-repo --path secrets.json --invert-paths # removes a file from all history
Wrong reflex: “I delete the file and recommit.” The secret remains readable via git show <ancien sha>:fichier. Only secret rotation resolves the risk.
Expert commands#
🔴 Expert level
git worktree add ../hotfix release/9.0 # multiple directories, one single repository
git stash push -m "wip" -- chemin/ # partial stash
git restore -SW --source=release/9.0 -- fichier
git diff --stat main...feature # three dots: from common ancestor
git log main..feature # two dots: what feature has extra
git log --left-right --cherry-pick main...feature # see "equivalent" commits
git cherry -v main feature # commits not yet applied (by patch-id)
git rev-list --count A..B
git reflog expire --expire=now --all && git gc --prune=now # purge (irreversible)
git update-index --assume-unchanged fichier # avoid: hides, does not protect
git notes add -m "Acceptance tested" <sha> # annotation outside commit
git archive --format=zip HEAD -o kit.zip # clean export of a state (without .git)
git describe --tags # v9.1.0-14-gabc1234
git maintenance start
git config --global alias.lg "log --oneline --graph --decorate --all"
git worktree is the BI expert's weapon when maintaining multiple product versions: one directory per version (see 05), a single repository, no destructive branch switching.
git describe generates a readable version number from the latest tag: useful for timestamping a kit (kit-9.1.0-14-gabc1234).
Classic pitfalls#
🔴 Expert level
| Symptom | Cause | Remedy |
|---|---|---|
| “rejected (non-fast-forward)” on push | The remote moved ahead | git fetch then pull --rebase or merge; no force |
| Diff “of the entire file” | Line endings or BOM changed | .gitattributes, core.autocrlf; see 06 |
File ignored despite .gitignore |
Already tracked | git rm --cached fichier |
| “Disappeared” branch | Abandoned detached HEAD | git reflog, git branch rescue <sha> |
| Repeated conflicts during a long rebase | Overlapping commits | rerere, or merge instead of rebase, or squash beforehand |
| Huge repository after importing binaries | History keeps everything | git filter-repo, LFS, git gc --aggressive (rarely useful) |
“Lost” merge after revert of a merge |
Git believes content is already merged | Re-revert the revert before merging again |
| “Almost identical” filenames on Windows | Case-insensitive system | core.ignorecase, rename in two steps |
| Path too long on Windows | 260-character limit | git config core.longpaths true, short root folders |
Commit message convention: a one-line title under 72 characters in the imperative, blank line, then a body explaining the why and not the what. Work item reference in footer (AB#12345 for Azure Boards).
Git on Azure DevOps, GitHub, and GitLab#
🔴 Expert level
| Capability | Azure Repos (Git) | GitHub | GitLab |
|---|---|---|---|
| Merge request | Pull request | Pull request | Merge request |
| Branch policies | Minimum reviewers, validation build, comment resolution, mandatory work item linking | Branch protection / rulesets, required checks | Protected branches, approval rules |
| Ticket integration | Native with Azure Boards (AB#) |
Issues, Projects | Issues, boards |
| CI/CD | Azure Pipelines (YAML) | Actions | GitLab CI |
| On-prem hosting | Azure DevOps Server | GitHub Enterprise Server | Self-managed GitLab |
| Folder-level permissions | Limited (repository level); path-based reviewer policies | CODEOWNERS | CODEOWNERS |
| Built-in AI | Copilot (in the IDE) | Copilot (PR, review, agent) | GitLab Duo |
Notable specifics:
- Azure Repos: in the same project, you can have multiple Git repositories and one TFVC repository (03). Branch policies (reviewers, builds, work item linking) cover much of what check-in policies did in TFVC. More details in Azure DevOps Server (ex-TFS) in Depth.
- GitHub: richest ecosystem (Actions, Dependabot, code scanning, Codespaces). Rulesets complement branch protection rules (both coexist and their rules stack; multiple rulesets can apply to the same branch).
- GitLab: integrated DevOps platform, a good choice if you want everything in one self-hostable tool.
- In all cases: protect
main, require PR + build, forbid force-push, tag releases, back up the repository.
Ratings /5 (context: team of 10 to 30 people, release cycles by version, Microsoft ecosystem already in place):
| Criterion | Azure Repos | GitHub | GitLab |
|---|---|---|---|
| Integration with Azure Boards work items | 5/5 | 3/5 | 2/5 |
| Richness of the extension ecosystem | 3/5 | 5/5 | 4/5 |
| Self-hosting for sovereignty requirements | 4/5 | 4/5 | 5/5 |
| Cost / license already acquired | 4/5 | 3/5 | 3/5 |
AI contribution#
Concrete AI use cases around Git.
| Use case | Gain | Risk | Maturity | Prerequisites | Guardrails |
|---|---|---|---|---|---|
| Generate a commit message from the diff | 3/5 | 2/5 | 4/5 | Small, focused diff | Review it: AI describes the what, not the business why |
| Summarize a pull request | 4/5 | 2/5 | 4/5 | Platform with Copilot or equivalent | Do not replace reading the diff |
| Assisted code review | 3/5 | 3/5 | 3/5 | Explicit team rules | False negatives; AI does not know your implicit business rule |
| Suggest conflict resolution | 3/5 | 4/5 | 3/5 | Context from both branches | Never accept without testing: a “plausible” merge can invert a business rule |
| Explain history (“why this line?”) | 4/5 | 2/5 | 4/5 | git blame/log provided |
Verify assumptions in linked tickets |
| Write the appropriate Git command (reset, rebase, filter-repo) | 4/5 | 4/5 | 4/5 | Model understanding | Destructive commands: read, simulate (-n), back up a branch |
| Draft release notes from commits | 4/5 | 2/5 | 4/5 | Commits linked to tickets | Functional review, human author sign-off |
Prompt examples:
Here is the staged diff (git diff --staged). Propose a commit message in French:
title ≤ 72 characters in the imperative, body explaining why, without inventing
a ticket. If the diff mixes multiple topics, say so and propose how to split it.
Here is the output of git log --oneline v9.0.0..v9.1.0 -- Datamart/.
Group by theme (bug fix, enhancement, technical) and draft release
notes for a functional consultant. Flag ambiguous entries instead of guessing.
Confidentiality: sending a diff containing secrets, customer data, or restrictively licensed code to an external AI service. Remedy: usage policy, company-approved models/instances, secret detection before sending.
AI does not replace: judgment about what should be committed, responsibility for the merge, business acceptance testing of the numbers, or the discipline of atomic commits (AI on a catch-all commit produces a catch-all message). See AI in Planning and Version Control Tools and the Cross-Functional Summary — AI Contribution.
See also#
- Overview of Version Control Systems · TFVC in Depth · Git vs TFVC: compare, migrate, coexist
- Branching Strategies · VCS and SQL / BI data
- CI/CD · Matrix: version control and branches
- Glossary · Rating
✻ Written by Claude Sonnet 5.5