Carla has spent three days on feature/multiple-delete. It is a long branch: it touches app.js, styles.css and the new dialog in ui-components. She has the code half-written, with unfinished functions and the application in a state where it does not even start.
And then the alert arrives: there is a fault in production and a fix has to be released now.
Her current routine is this:
git stash -u
git switch main
git switch -c fix/urgent
# ... fix, test, commit, publish ...
git switch feature/multiple-delete
git stash pop
# ... mentally reconstruct where she was ...Ten times a day. And every round trip has its toll: the stash with untracked files sometimes gives conflicts when you restore it, the mental context has to be rebuilt, the development server restarts, and since there has been a submodule (lesson 06-05) every branch switch also drags its update along.
Her next idea is to clone the repository twice. It would work, but as we shall see it has a real cost. Git offers something better and fairly little known: several working directories sharing a single object database. It is called git worktree, and with this lesson we close the module.
Contents
- What a worktree is
worktree add: the first additional directory- Comparison with cloning twice
- The complete set of commands
- The key rule: one branch, one worktree
- How it looks from the inside
- Real use cases
- Interaction with
stash, submodules and hooks - Closing module 6
- What a worktree is
Let us recall the structure from lesson 01-03. A Git repository has two clearly distinct parts:
- The
.git/directory: the objects, the references, the configuration, the reflog. The real repository. - The working tree: the files you see and edit, which are the reflection of one specific commit.
So far we have assumed there is one of each. But it is not obligatory:
A worktree is an additional working directory, with its own
HEAD, its own index and its own files, which shares the object database and the references with the original repository.
flowchart TD
subgraph BD[".git/ — a single database"]
O["Objects: blobs, trees, commits"]
R["References: branches, tags, remotes"]
C["Configuration and reflog"]
end
subgraph W1["~/projects/task-manager"]
H1["HEAD → feature/multiple-delete"]
I1["Its own index"]
F1["app.js, styles.css… (half-finished)"]
end
subgraph W2["~/projects/task-manager-urgent"]
H2["HEAD → fix/urgent"]
I2["Its own index"]
F2["app.js, styles.css… (clean)"]
end
W1 --> BD
W2 --> BD
What is shared: objects, branches, tags, remotes, git fetch, the repository's configuration, the references' reflog.
What is each worktree's own: HEAD, the index, the files on disk, the state of operations in progress (a half-finished merge, a stopped rebase) and the stash stack.
The original directory is called the main worktree; the added ones, linked worktrees. Functionally they are almost identical.
worktree add: the first additional directory
worktree add: the first additional directoryCarla solves her problem with one command:
What has just happened:
- The directory
~/projects/task-manager-urgent/has been created. - It contains a complete working copy of the project in
main's state. - The branch
fix/urgenthas been created frommainand is checked out there. - The original directory has not been touched at all: it is still on
feature/multiple-delete, with all its half-finished work intact.
/home/carla/projects/task-manager c8f2a1e [feature/multiple-delete] /home/carla/projects/task-manager-urgent c8f2a1e [fix/urgent]
Carla now works in the second directory entirely normally:
cd ../task-manager-urgent
# ... fix app.js ...
git commit -am "Fix deletion when the task has subtasks"
git push -u origin fix/urgentAnd when she is finished, she goes back:
On branch feature/multiple-delete Changes not staged for commit: modified: app.js modified: styles.css Untracked files: new-dialog.js
Exactly as she left it. No stash, no pop, no rebuilding the context: the editor, the development server and the browser window are all still open where they were.
The ways of invoking add:
| Command | What it does |
|---|---|
git worktree add <path> <existing-branch> |
Opens that branch in the new directory |
git worktree add <path> |
Creates a branch with the directory's name |
git worktree add -b <new-branch> <path> [<start-point>] |
Creates the branch and opens it |
git worktree add -B <branch> <path> [<start-point>] |
Like -b, but resets it if it already exists |
git worktree add --detach <path> <commit> |
Detached HEAD at that commit, with no branch |
git worktree add --track -b <branch> <path> origin/<branch> |
Creates the branch tracking the remote |
And a useful one when the branch comes from the remote:
Git detects that feature/export exists in origin and automatically creates a local tracking branch (the --guess-remote behaviour, which can be pinned with git config worktree.guessRemote true).
- Comparison with cloning twice
The obvious alternative was another git clone. The comparison explains why worktree is better almost always:
git worktree add |
A second git clone |
|
|---|---|---|
| Disk space | Only the working tree's files | Files + the whole object database duplicated |
| Shared objects | Yes: a single copy | No: two independent copies |
| Visible branches | The same ones in every worktree | Each clone has its own |
| Tags | Shared | Duplicated and able to drift apart |
git fetch |
A single one updates them all | One per clone |
| Remotes and credentials | Shared | They have to be configured in each one |
| Local configuration | Shared (with nuances) | Independent per clone |
| A commit in A, visible in B? | Immediately | Only after push + fetch |
| Stash | Independent per worktree | Independent |
| Hooks | Shared (a single .git/hooks) |
One per clone |
| Creation time | Seconds (there is no transfer) | However long the full clone takes |
| Risk of drifting apart | None: it is one single repository | Real |
The point that is most underestimated is "a commit in A, visible in B?". With two clones, to take a commit from one to the other you have to go through the server. With worktrees, as soon as Carla commits in one directory, that commit already exists for the other: she can run git cherry-pick, git rebase or git log on it immediately, with no network in between.
The space saving, in figures: if .git/ takes up 400 MB (long history, the odd binary) and the working tree 15 MB, an extra clone costs 415 MB and a worktree 15 MB. In large repositories the difference stops being incidental.
There used to be
git clone --shared/--referencefor sharing objects between clones. It works, but it is fragile: if the reference repository is moved or cleaned up, the dependent clone can end up corrupted.git worktreeis the modern, safe solution to the same problem, and the one to use.
- The complete set of commands
list
/home/carla/projects/task-manager c8f2a1e [feature/multiple-delete] /home/carla/projects/task-manager-urgent 3d8f1a6 [fix/urgent] /home/carla/projects/task-manager-v1 b7e2c4a (detached HEAD) /home/carla/projects/task-manager-old a1b2c3d [experiment] prunable
The prunable marker indicates that the directory no longer exists on disk and its registration can be cleaned up.
remove
The correct way of deleting a worktree:
It deletes the directory and its registration. If there are uncommitted changes, it refuses — which is a protection, not a nuisance:
And be careful: remove does not delete the branch. That is separate:
prune
If you deleted the directory by hand with rm -rf (which works, but leaves the registration behind), prune cleans up the remnants:
Git also runs it by itself from time to time, and the grace period is controlled by gc.worktreePruneExpire (three months by default).
move
It moves the directory and updates the internal paths. Moving it with a plain mv breaks the links, so always use this command.
lock and unlock
git worktree lock ../task-manager-usb --reason "It is on the external backup drive"
git worktree unlock ../task-manager-usbA locked worktree cannot be pruned or moved. It is exactly for the case of a worktree on a removable drive or a network share: when the drive is not mounted, the directory "does not exist" and prune would simply remove it from the registration. lock prevents that, and the reason shows up in git worktree list --porcelain so that people know why.
repair
It rebuilds the internal links when something has broken them: you have moved directories by hand, you have restored a backup, or you have renamed the main repository.
Summary table:
| Command | What it does |
|---|---|
git worktree add <path> [<branch>] |
Creates a new worktree |
git worktree list |
Lists them all, with their branch and their commit |
git worktree remove <path> |
Deletes one (with --force if there are changes) |
git worktree prune |
Cleans up registrations of already-deleted worktrees |
git worktree move <source> <target> |
Moves one, updating the paths |
git worktree lock/unlock <path> |
Protects one from prune and move |
git worktree repair [<paths>] |
Repairs broken internal links |
- The key rule: one branch, one worktree
This is the fundamental restriction, and it has to be understood because it explains half the error messages you will see:
The same branch cannot be checked out in two worktrees at once.
And the same when creating one:
Why the restriction exists. A branch is a pointer that moves on when you commit (lesson 03-01). If two worktrees had the same branch checked out, a commit in one would move the branch under the other's feet: the second would find its HEAD pointing at a commit it had not created, and its working tree would stop corresponding to anything coherent. Git forbids the situation rather than letting it happen.
It is not a limitation, it is a protection. And it has three ways out when you genuinely need the same code twice:
# A) In detached HEAD: with no branch to move, there is no conflict
git worktree add --detach ../review-read-only feature/multiple-delete
# B) At a specific commit, which is the same thing
git worktree add --detach ../version-1.0 v1.0.0
# C) A new branch from the same point
git worktree add -b experiment/another-route ../experiment feature/multiple-deleteOption A is the most used: to read or build a branch you do not need to have it checked out as a branch.
And a practical consequence worth knowing: git branch -d refuses to delete a branch that is checked out in another worktree, and git rebase or git merge cannot operate on it from outside either. To find where it is:
- How it looks from the inside
We pick up lesson 01-04 and the data model again, because the mechanism is elegant and explains everything above.
In the main worktree, .git is a directory:
In a linked worktree, .git is a text file:
A single line saying: "my repository is over there". It is exactly the same mechanism used by the submodules from lesson 06-05, where the submodule's .git is a file pointing at the parent's .git/modules/<name>.
And in the main repository:
| File | Content |
|---|---|
HEAD |
That worktree's own HEAD |
index |
Its own index (the staging area) |
gitdir |
The absolute path of the working directory it serves |
commondir |
Path to the common .git, where the objects and the refs are |
logs/HEAD |
That worktree's reflog |
And there is the complete explanation of the model:
HEADandindexare per worktree → each one can be on a different branch and have its own staged changes.objects/andrefs/are in thecommondir→ the branches, the tags and the objects are the same for all of them, and that is why a commit in one is instantly visible in the other.
This also makes clear why the restriction in section 5 is unavoidable: there is a single file refs/heads/feature/multiple-delete, and it cannot be the HEAD of two directories committing separately.
There is even a notation for querying another worktree's HEAD:
git rev-parse main@{main-worktree} # advanced syntax, rarely needed
git worktree list --porcelain # the usual wayAnd a note about configuration: by default, .git/config is shared by all the worktrees. If you need one of them to have its own configuration, there is extensions.worktreeConfig:
It is an advanced and infrequent case, but it is worth knowing it exists if you come across a config.worktree inside .git/worktrees/<name>/.
- Real use cases
Case 1: the hotfix without touching what you have half-finished
Carla's, and the most common. A permanent worktree for emergencies:
When the alert arrives, cd ../task-manager-hotfix, git pull, create the branch, fix it and publish. No stash, no branch switching, no losing the context.
Case 2: comparing two versions while they run
Bruno has to check whether a performance problem existed in version 1.0:
# Terminal 1
cd ~/projects/task-manager && python3 -m http.server 8000
# Terminal 2
cd ~/projects/task-manager-v1 && python3 -m http.server 8001Two servers, two browser tabs, both versions running at once. Comparing like that is incomparably more reliable than switching branches back and forth and trying to remember what it was like.
Case 3: building one branch while you work on another
A complete build of task-manager takes four minutes. With a single directory, those four minutes are spent waiting, because any edit spoils the result. With worktrees:
git worktree add ../task-manager-build feature/multiple-delete-rc
cd ../task-manager-build && npm run build &
cd ~/projects/task-manager # carry on working while it buildsCase 4: reviewing a colleague's branch
Ana has to review Bruno's pull request, but she is halfway through her own work:
git fetch
git worktree add --detach ../review origin/feature/export
cd ../review
# ... run it, read it, test it ...
cd .. && git worktree remove reviewIt would work without --detach too, but you do not need a local branch in order to review, and this way you avoid accumulating review branches.
Case 5: a long rebase without blocking your work
A git rebase -i with conflicts (lesson 05-02) leaves the repository in an intermediate state. If something urgent comes up, you are trapped: rebase --abort and start again. With a dedicated worktree, the rebase stays paused in its directory and you work in another. Since the state of operations in progress is each worktree's own, they do not interfere.
Case 6: bisect without stopping
git bisect (lesson 06-02) does dozens of checkouts in your directory. If it is a long bisection with builds, you can launch it in a separate worktree:
git worktree add --detach ../task-manager-bisect main
cd ../task-manager-bisect
git bisect start main v1.0.0
git bisect run /tmp/test-delete.shMeanwhile, your main directory stays on your branch, untouched. When you are finished, git bisect reset and git worktree remove.
Case 7: documentation on an orphan branch
Some projects publish their documentation on a separate branch (gh-pages and the like). With worktrees:
The documentation is edited in its directory and the code in its own, with separate histories and never switching branch.
- Interaction with
stash, submodules and hooks
stash, submodules and hooksWith git stash
The stash stack is each worktree's own. A git stash list in the main directory does not show what was stashed in another:
Technically, refs/stash is stored per worktree. It is consistent with the model — a stash is half-finished work from one specific tree — but it is surprising the first time.
And the practical conclusion of all this: worktrees do not replace stash, but they greatly reduce the need for it. stash is still the right tool for setting something aside for two minutes within the same branch; the worktree is the right one for working on two branches for days.
| Situation | Tool |
|---|---|
Setting changes aside for two minutes to do a pull |
git stash |
| Trying something quickly on the same branch | git stash |
| Working on two branches for hours or days | git worktree |
| Dealing with emergencies without losing the context | git worktree |
| Comparing two versions while they run | git worktree |
| Saving work before switching machine | git stash or a WIP branch |
With submodules
Worktrees and submodules (lesson 06-05) coexist, but there are two things to know:
- Submodules do not initialise themselves in a new worktree. After the
add, you need:
git worktree add ../task-manager-urgent -b fix/urgent main
cd ../task-manager-urgent
git submodule update --init --recursive- The submodules' internal repositories live in the common
.git/modules/, so the objects are shared just like the parent's: initialisation does not download anything from the network again, it only does acheckout. It is fast.
Support for worktrees inside submodules has improved a great deal in recent versions of Git, but it is still the territory where the most oddities show up. If something goes out of place, git worktree repair usually sorts it out.
With hooks
Hooks are shared: there is a single .git/hooks/ (or a single core.hooksPath, lesson 06-01) for all the worktrees. An installed pre-commit works in all of them automatically, which is an advantage.
The nuance: a hook that assumes absolute paths or takes a specific directory for granted may get confused. Hooks run with the working directory set to the root of the active worktree, so using relative paths is the right thing to do. And if a hook needs to tell where it is:
git rev-parse --show-toplevel # root of the current worktree
git rev-parse --git-common-dir # the shared .git
git rev-parse --git-dir # this worktree's .git/worktrees/<name>With performance and maintenance
A brief note, because the full topic belongs to another lesson: worktrees share the object database, so a git gc affects them all and Git takes care not to remove objects referenced from any of them. A worktree that is registered but whose directory no longer exists can, on the other hand, keep objects alive unnecessarily: that is why it is worth running git worktree prune from time to time.
Everything relating to
git gc,git maintenanceand performance in large repositories is the subject of lesson 08-06: Performance Tips; partial clones,sparse-checkoutand the scaling techniques, that of 10-04.
- Closing module 6
With this lesson you close the block of tools. Looking back at what has changed in the task-manager team's way of working:
- Hooks (06-01): automatic checks in
pre-commit,commit-msgandpre-push, version-controlled withcore.hooksPath. They help against oversight, but what is mandatory gets checked on the server. git bisect(06-02): a binary search that turns 214 commits into 8 tests, automatable withbisect runand its exit codes.git blame(06-03): the history of each line, with-w,-Cand--ignore-revto see through the noise, andgit log -Lto see the complete evolution.- Advanced
git logand aliases (06-04):--graph,--first-parent,--simplify-by-decoration,--left-right, coloured formats,shortlog, and the aliases that turn all of that into a single word. - Submodules (06-05): a pointer to a commit of another repository, with exact reproducibility in exchange for friction, and compared with subtree, packages and the monorepo.
git worktree(06-06): several working directories over a single object database.
And one idea that runs through the whole module: Git's history is not just a record of what happened, it is a queryable database. Who wrote each line, in which commit a behaviour changed, what has been integrated into main this month, which exact version of the library version 1.0 used. All of those questions have an exact answer, and you now know how to ask for it.
What comes next
The team has mastered the tool. Ana, Bruno and Carla know how to build the history, manipulate it with judgement, query it thoroughly and automate checks over it.
What they have not agreed yet is how to work together. And those questions are no longer technical ones:
- When Bruno finishes a feature, how does he propose it? Does he push straight to
main? Does he open a pull request? And what if he has no write access to the repository? - When Ana reviews Carla's work, what does she look at, how does she comment and when does she approve? What does a reviewer do beyond repeating what the linter already says?
- Which branches exist and what is each one for? Is there a
developbranch? Release branches? Or does everybody integrate intomainseveral times a day? - When is a version released? What has to have been passed before a change reaches production?
There is no single answer: there are different workflows, each with its own logic, its advantages and its type of team. An open source project with hundreds of external contributors cannot work like a team of three people deploying five times a day.
In module 7: Collaboration and Workflow Strategies we shall see forks and pull requests as the mechanism for proposing changes, code reviews and how they are done well, and the three great branching models — Git Flow, GitHub Flow and Trunk Based Development — compared with judgement so that you know which fits which situation. And we shall close with continuous integration: the automatic checks that, this time, nobody can skip with a --no-verify.
We begin with how a change is proposed, in lesson 07-01: Forks and Pull Requests.
Common Mistakes and Tips
Mistake 1: trying to open the same branch in two worktrees. Git prevents it and tells you where it is checked out. Use --detach if you only want to read or build.
Mistake 2: deleting the directory with rm -rf. It works, but it leaves the registration dirty. Use git worktree remove, or git worktree prune afterwards.
Mistake 3: moving the directory with mv. It breaks the internal links. Use git worktree move, or git worktree repair if you have already done it.
Mistake 4: forgetting that remove does not delete the branch. Removing the worktree leaves the branch alive; delete it separately with git branch -d.
Mistake 5: expecting submodules to initialise themselves. You have to run git submodule update --init --recursive in every new worktree.
Mistake 6: looking for a stash in the wrong worktree. The stack is each one's own. If it is not there, look in the other directory.
Mistake 7: creating worktrees inside the repository itself. git worktree add ./temp works, but the directory shows up as untracked content of the main repository. Create them outside, as siblings of the original directory.
Mistake 8: accumulating forgotten worktrees. They take up disk space and keep branches occupied. git worktree list from time to time, and remove the surplus ones.
Tip 1: adopt a naming convention. task-manager, task-manager-hotfix, task-manager-v1. With ../<project>-<purpose> you never get lost.
Tip 2: keep a permanent worktree for emergencies. Always on main, always clean. It is the one that will save your day most often.
Tip 3: create an alias. With what you learned in lesson 06-04:
git config --global alias.wt "worktree list"
git config --global alias.new '!f() { git worktree add "../$(basename "$PWD")-$1" -b "$1"; }; f'git new urgent-fix creates ../task-manager-urgent-fix with that branch.
Tip 4: --detach for anything read-only. Reviewing, building, comparing or bisecting does not need a local branch, and that way you do not run into the one-branch-per-worktree rule.
Tip 5: one git fetch is enough for all of them. It is a single repository: do not repeat the fetch in each directory.
Tip 6: check your version of Git. worktree has existed since 2.5, but move, remove and repair came later (2.17 and 2.30). On old versions, some operations are manual.
Exercises
Exercise 1: the hotfix flow
- Create a repository with
mainand afeature/long-onebranch with uncommitted changes (modified and untracked). - Without doing a
stash, create a worktree at../project-hotfixwith a new branchfix/urgentfrommain. - Commit a fix in the new worktree.
- Go back to the original directory and check that your uncommitted changes are still exactly as they were.
- Check from the original directory that the hotfix commit already exists (
git log fix/urgent), without having done anypushorfetch. - Delete the worktree and the branch.
Exercise 2: the one-branch-per-worktree restriction
- With the previous repository, try to create a worktree with a branch that is already checked out in another. Note down the error message.
- Get the same code into a second directory using
--detach. - Check with
git worktree listthat one shows up with a branch and the other as(detached HEAD). - Try to delete with
git branch -da branch checked out in another worktree and observe what happens.
Exercise 3: the anatomy from the inside
- In a linked worktree, check that
.gitis a file and show its content. - Locate the corresponding directory in the main one's
.git/worktrees/and list its content. - Compare the
HEADof both worktrees. - Do a
git stashin one and check thatgit stash listin the other is empty. - Delete a worktree with
rm -rf, check that it still appears ingit worktree list(marked asprunable) and clean it up withprune.
Solutions
Solution 1:
mkdir /tmp/practice-worktree && cd /tmp/practice-worktree
git init -q -b main
echo "<html><body></body></html>" > index.html
echo "console.info('start-up');" > app.js
git add . && git commit -q -m "Add the application skeleton"
git switch -qc feature/long-one
echo "// half-finished work, does not build" >> app.js
echo "unfinished draft" > notes.txt
git status --shortcd ../project-hotfix
git status --short # clean
echo "console.info('fix applied');" >> app.js
git commit -qam "Fix the start-up when the container is missing"
git log --onelineUntouched. No stash, no pop.
The commit made in the other directory is visible immediately: it is the same object database. With two clones it would have needed push + fetch.
The branch is still alive: remove does not delete it.
Solution 2:
Same commit, same content, no conflict: in ../other there is no branch that could move.
Git protects the branch checked out in any worktree, not just the current one.
Solution 3:
cat /tmp/practice-worktree/.git/worktrees/other/HEAD
cat /tmp/practice-worktree/.git/HEAD
cat /tmp/practice-worktree/.git/worktrees/other/commondirThe linked worktree has a direct hash (detached), the main one a symbolic reference to its branch, and commondir points at the shared .git where the objects and the refs are.
The registration is still there, marked as prunable.
Clean. With git worktree remove instead of rm -rf, this last step would not have been necessary.
Conclusion
git worktree solves an everyday problem with a simple, well-built idea. The essentials:
- A worktree is an additional working directory with its own
HEADand index, which shares objects and references with the original repository. - Compared with cloning twice: it does not duplicate the object database, it shares branches, tags, remotes and hooks, a single
fetchserves them all, and a commit made in one is instantly visible in the other, without going through the server. git worktree add <path> [<branch>]creates it in seconds;-bcreates a new branch and--detachopens a commit with no branch.- The complete set is
add,list,remove,prune,move,lock/unlockandrepair. Useremoverather thanrm -rfandmoverather thanmv;lockprotects worktrees on removable drives. - A branch cannot be checked out in two worktrees at once. It is not a capricious limitation: it stops a commit from moving the branch under another directory's feet. The way out for reading and building is
--detach. - Inside, the linked worktree's
.gitis a file with agitdir:line pointing at.git/worktrees/<name>, where itsHEAD, itsindexand its reflog live; the objects and the refs are in the sharedcommondir. It is the same mechanism the submodules use. - Use cases that earn their place: a hotfix without losing the context, comparing two versions while they run, building one branch while you work on another, reviewing a colleague's branch, and isolating a long rebase or a bisection.
- The stash is each worktree's own; hooks are shared; submodules have to be initialised in every new worktree.
- And the relationship with
stash: it does not replace it, but it greatly reduces its use.stashfor minutes within a branch;worktreefor days across several.
With this, module 6 and the course's technical block come to a close. The task-manager team now knows how to build, manipulate, query and automate its history. What it needs now is to reach an agreement: how a change is proposed, how it is reviewed and which branching flow to follow. That is module 7, and it begins in lesson 07-01: Forks and Pull Requests.
Mastering Git: From Beginner to Advanced
Module 1: Introduction to Git
- What Is Git?
- Installing Git
- Basic Git Terminology
- The Git Data Model
- Configuring Git
- Initial Configuration
Module 2: Basic Git Operations
- Creating a Repository
- Cloning a Repository
- The Basic Git Workflow
- Staging and Committing Changes
- Inspecting Changes with git diff
- Viewing Commit History
Module 3: Branching and Merging
- Understanding Branches
- Creating and Switching Branches
- Merging Branches
- Merge Strategies
- Resolving Merge Conflicts
- Branch Management
Module 4: Working with Remote Repositories
- Understanding Remote Repositories
- Adding a Remote Repository
- Authenticating with Remote Repositories
- Fetching and Pulling Changes
- Pushing Changes
- Tracking Branches
Module 5: Advanced Git Operations
Module 6: Git Tools and Techniques
- Using Git Hooks
- Git Bisect
- Git Blame
- Git Log and Aliases
- Git Submodules
- Multiple Working Copies with git worktree
Module 7: Collaboration and Workflow Strategies
- Forks and Pull Requests
- Code Reviews with Git
- The Git Flow Workflow
- GitHub Flow
- Trunk Based Development
- Continuous Integration with Git
Module 8: Git Best Practices and Tips
- Writing Good Commit Messages
- Keeping a Clean History
- Ignoring Files with .gitignore
- File Attributes with .gitattributes
- Security Best Practices
- Performance Tips
Module 9: Troubleshooting and Debugging
- Common Git Problems
- Undoing Changes
- Resolving Divergence with the Remote
- Recovering Lost Commits
- Dealing with Corrupted Repositories
- Advanced Debugging Techniques
