In the previous lesson Ana turned her folder into a repository and created the project's first commit. She has worked on it for a few more days since then, and the history of task-manager already holds several commits. The team has also published the repository on an internal server so that it can be shared.
Today Bruno joins, and he works on macOS. Bruno needs the project on his laptop, but not just any copy: he needs the files and the whole history, so that he can look up why each change was made, compare versions and, later on, contribute his own. Downloading a .zip would give him today's files and nothing else: no history, no context, no ability to collaborate.
The right tool is git clone. In this lesson we will see what it really does — which is quite a bit more than copying files — which protocols exist for reaching a remote repository and how they differ, the options that come up most in daily work (--branch, --depth, --single-branch) and, above all, when to use clone and when to use init, a classic beginner's doubt.
Contents
- Bruno's situation
git clonein its simplest form- What
git clonereally does, step by step - Protocols: HTTPS, SSH and a local path
- Cloning into a directory with a different name
- Cloning a specific branch with
--branch - Shallow clones with
--depth --single-branchand other useful optionsinitversusclone: the conceptual difference- Checks to run after cloning
- Bruno's situation
The task-manager repository is published on the company's internal server. Bruno has three possible addresses for reaching it:
https://git.example.com/team/task-manager.git (HTTPS) [email protected]:team/task-manager.git (SSH) /Volumes/shared/repos/task-manager.git (local path, shared disk)
All three point at the same repository: a bare repository — no working tree, as we saw at the end of the previous lesson — acting as the team's central point. How Ana's repository got there, and how these addresses are managed, is the subject of module 4; here we focus on the other side of the operation: bringing it home.
Bruno already has Git installed and configured with his identity, following what we saw in module 1:
git config --global user.name
# → Bruno Salas
git config --global user.email
# → [email protected]
git clone in its simplest form
git clone in its simplest formBruno moves into the folder where he keeps his projects and runs:
Cloning into 'task-manager'... remote: Enumerating objects: 24, done. remote: Counting objects: 100% (24/24), done. remote: Compressing objects: 100% (16/16), done. remote: Total 24 (delta 6), reused 0 (delta 0), pack-reused 0 Receiving objects: 100% (24/24), 4.21 KiB | 4.21 MiB/s, done. Resolving deltas: 100% (6/6), done.
And that is it: in a few seconds he has the complete project.
c5d9b1e (HEAD -> main, origin/main, origin/HEAD) Document installation in the README 4e7f2a9 Add task deletion to the list 8b6d3c2 Add base styles for the list 1a4c8d6 Add initial task manager structure
There is Ana's complete history, including the initial commit we created in the previous lesson and the recurring commit "Add task deletion to the list" touching app.js. Bruno has not received a photograph of the project: he has received the project and its memory.
And the working tree is clean from the very first moment:
Decoding the clone output
The lines that scroll past during the download are not noise; they describe what the server is doing:
| Line | What it means |
|---|---|
Enumerating objects |
The server works out which objects need sending |
Counting objects |
It counts them and prepares the transfer |
Compressing objects |
It compresses them into a packfile so that fewer bytes go over the wire |
Total 24 (delta 6) |
24 objects, 6 of which travel as differences against others |
Receiving objects |
Download progress on your machine |
Resolving deltas |
Your Git rebuilds the complete objects from those differences |
That (delta 6) connects with something we mentioned in The Git Data Model: conceptually Git stores snapshots, but to store and transmit them it uses packfiles, which do compress some objects against others. It is an invisible optimisation: once Resolving deltas finishes, Bruno's disk holds exactly the same objects as Ana's.
- What
git clone really does, step by step
git clone really does, step by stepA clone looks like a simple "download the folder", but it is five chained operations. Understanding them heads off an enormous amount of later confusion.
graph TD
A["1 · Create the directory<br/>task-manager/"] --> B["2 · Create .git/ inside it<br/>(like a git init)"]
B --> C["3 · Download ALL the objects<br/>and references from the source"]
C --> D["4 · Register the source<br/>as the remote 'origin'"]
D --> E["5 · Create the local branch 'main'<br/>and write its files out to the<br/>working tree"]
Step 1: create the directory
Unless you say otherwise, Git uses the last segment of the URL without the .git suffix. From https://git.example.com/team/task-manager.git comes the folder task-manager. If that folder already exists and is not empty, the clone fails:
This is a deliberate safeguard: git clone never overwrites existing work.
Step 2: initialise the repository
Internally, a clone starts by doing the equivalent of git init in the destination directory. That is why the result has exactly the same anatomy we studied yesterday:
A few items show up that a plain git init did not produce — index, logs/, packed-refs, FETCH_HEAD — simply because here there is actual content: there are staged files, there are references to pack and a network operation has taken place.
Step 3: download the objects
This is where the important part happens. Git downloads the entire object database: every blob, every tree, every commit and every tag in the source repository, together with its references.
This is what makes Git a distributed system, as we saw in What is Git?: after the clone, Bruno's laptop holds a complete, fully functional copy of the project. He can browse the history, compare versions from a year ago or create commits with no network connection. In a centralised system such as SVN, almost any of those operations would require talking to the server.
We can verify it:
All 24 objects are there, inside a packfile (in-pack: 24).
Step 4: register the origin remote
The clone records where it came from, under the conventional name origin:
origin https://git.example.com/team/task-manager.git (fetch) origin https://git.example.com/team/task-manager.git (push)
And it writes it into the repository's local configuration:
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
precomposeunicode = true
[remote "origin"]
url = https://git.example.com/team/task-manager.git
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "main"]
remote = origin
merge = refs/heads/mainorigin is not a reserved word in Git: it is merely the default name clone gives the remote. You could call it central, upstream or server. The convention is so well established that it is worth sticking to.
The [branch "main"] section means the local branch main tracks origin/main. That is what lets git status tell you whether you are ahead of or behind the server. All of this — remotes, tracking branches, fetch, pull, push — is the content of module 4; here it is enough to know that the clone sets it up on its own.
Step 5: create the local branch and write out the files
Finally, Git creates a local branch matching the source's default branch (main), points HEAD at it and writes that commit's files to disk, populating the working tree and the staging area at the same time.
That last detail explains why git status comes out clean straight after a clone: all three areas — repository, staging area and working tree — hold the very same content.
- Protocols: HTTPS, SSH and a local path
The URL you hand to git clone determines how the data travels. These are the three cases you will meet in practice:
| HTTPS | SSH | Local path | |
|---|---|---|---|
| Form | https://host/group/repo.git |
git@host:group/repo.git |
/path/to/repo.git or file:///path |
| Authentication | Username + access token | Public/private key pair | Filesystem permissions |
| Usual port | 443 | 22 | — |
| Gets through firewalls | Almost always | Sometimes blocked | — |
| Asks for credentials when cloning | Yes, unless the repository is public | No, if the key is loaded | No |
| Day-to-day convenience | Medium (needs a credential helper) | High, once set up | Maximum |
| Typical use | First contact, CI, restrictive networks | A developer's daily work | Backups, testing, shared disks |
HTTPS
It is the most universal option because port 443 is open practically everywhere. Cloning a private repository will ask you for credentials:
Cloning into 'task-manager'... Username for 'https://git.example.com': bruno.salas Password for 'https://[email protected]':
One important warning: on modern platforms (GitHub, GitLab and the like) that "password" is not your account password but a personal access token. To avoid retyping it every time, use the credential.helper we configured in Initial Configuration.
SSH
Notice that the short SSH form has no :// and uses a colon to separate the host from the path:
git clone [email protected]:team/task-manager.gitIt relies on a pair of cryptographic keys: the public one is registered on the server and the private one stays on your machine. Once it is set up, cloning and working never ask for a password again. It is the preferred option for daily work. Generating the keys and registering them is covered in Authenticating with Remote Repositories.
There is also an equivalent long form, which does carry :// and lets you specify a port:
git clone ssh://[email protected]:2222/team/task-manager.gitLocal path
You can clone from a folder on your own disk or from a network-mounted share:
It is blindingly fast and needs neither network nor credentials. It has one useful quirk: when source and destination sit on the same filesystem, Git uses hard links for the objects instead of copying them, so the clone takes up almost no space. If you would rather have a genuinely independent copy:
And if you write the path with the file:// prefix, Git uses the same transfer machinery it would use against a remote server, which is handy for testing:
Careful when cloning from a non-bare repository. You can clone from a normal repository (straight from Ana's
~/projects/task-manager, for instance), and it works: you get the whole history. But you only get what has been committed: any changes Ana has left uncommitted in her working tree or staging area do not travel. This is a reliable source of surprises — "I cloned your repo and your last change is missing" almost always means "you had not committed it".
- Cloning into a directory with a different name
Just add the name you want as a second argument:
The repository is identical; only the folder holding it changes. This helps in three situations:
- Avoiding collisions: you already have a
task-managerfolder belonging to another project. - Keeping two copies of the same repository, one to work in and another to consult an old version without disturbing the first. (For this particular case there is a purpose-built tool, git worktree, which you will meet in module 6.)
- Unhelpful names: if the repository is called
web, you might preferweb-client-acme.
If you want to clone into the current directory rather than a new one, use . as the destination. The directory must be empty:
- Cloning a specific branch with
--branch
--branchBy default a clone leaves you sitting on the source's default branch (main). With --branch (or -b) you pick another one:
Two important clarifications:
- The whole repository is downloaded anyway.
--branchdoes not filter what arrives; it only decides which branch you land on. The others are still there, still reachable. - It accepts tags too. Hand it a tag name and you will get the exact content of that version, but in detached HEAD state (you will not be on any branch):
That is perfectly fine for inspecting a released version. Exactly what that state means and how to leave it belongs to Understanding Branches and Tagging Commits.
- Shallow clones with
--depth
--depthOn a project with years of history, cloning the lot can mean downloading hundreds of megabytes you may not need. --depth limits how many commits come across:
Cloning into 'task-manager'... remote: Enumerating objects: 6, done. remote: Total 6 (delta 0), reused 0 (delta 0) Receiving objects: 100% (6/6), 1.83 KiB | 1.83 MiB/s, done.
Compare that with the full clone in section 2: 6 objects instead of 24. Only the latest commit has arrived.
The word grafted marks the point where the history is cut off artificially. That commit appears to have no parent, even though it does have one in the original repository.
What a shallow clone costs you
| Operation | Full clone | --depth 1 |
|---|---|---|
| Seeing the current files | Yes | Yes |
| Building / running the project | Yes | Yes |
| Creating new commits | Yes | Yes |
Complete git log |
Yes | No, only the latest |
Reliable git blame |
Yes | No |
git bisect to hunt down a bug |
Yes | No |
| Comparing against old versions | Yes | No |
| Download size | 100 % | Minimal |
When to use it and when not to
Yes:
- Continuous integration. A CI server builds and throws the copy away; the history is dead weight to it. This is by far the most common use.
- Containers and deployment images, where every megabyte counts.
- A one-off look at an enormous project when all you want is today's code.
No:
- Your daily working copy. You lose
blame, you losebisectand you lose any comparison with the past — three of the reasons to use Git in the first place.
Turning a shallow clone into a full one
If you change your mind, there is no need to clone again:
Or fetch more depth than you currently have:
Both commands belong to module 4; we mention them here so that you know the decision is reversible.
A modern note. Recent versions of Git offer partial clones (
--filter=blob:none), which download the whole commit graph but fetch file contents only when they are needed. They keeplog,blameandbisectworking and are usually a better choice than--depthfor large repositories. We will look at them in Scaling Git for Large Projects.
--single-branch and other useful options
--single-branch and other useful options--single-branch downloads only the branch you name, ignoring the rest:
Unlike --depth, it keeps the entire history of that branch; what it avoids is dragging in branches you do not care about. In a repository with two hundred working branches, the difference is noticeable.
An important note: --depth implies --single-branch automatically, unless you add --no-single-branch.
A round-up of the most frequent options:
| Option | Effect | Typical use |
|---|---|---|
-b, --branch <name> |
Lands you on that branch or tag | Working on develop from minute one |
--depth <n> |
Only the last n commits |
Continuous integration |
--single-branch |
One branch only, with all its history | Repositories with many branches |
--no-checkout, -n |
Downloads but does not write out the files | Preparing the repository before populating it |
--bare |
A clone with no working tree | Creating a replica to serve from |
--mirror |
Like --bare, but an exact replica of every reference |
Backups, migrations |
--recurse-submodules |
Clones the submodules as well | Projects with submodules |
--origin <name>, -o |
Gives the remote a different name | When origin already means something else |
--quiet, -q |
No progress bars | Scripts |
A couple of worked examples:
# Full backup of the team repository
git clone --mirror https://git.example.com/team/task-manager.git task-manager-backup.gitThis creates a bare repository holding every reference from the source, not just the branches. It is the usual way to migrate a repository from one server to another.
# Cloning with the remote named "central" instead of "origin"
git clone --origin central https://git.example.com/team/task-manager.git
git remote -v
# → central https://git.example.com/team/task-manager.git (fetch)
# → central https://git.example.com/team/task-manager.git (push)
init versus clone: the conceptual difference
init versus clone: the conceptual differenceThis is the most common confusion at the start, and one question settles it: does the project already exist somewhere?
git init |
git clone |
|
|---|---|---|
| Starting point | Nothing, or a folder with your own files | A repository that already exists |
| Resulting history | Empty: zero commits | Complete, identical to the source's |
| Remote configured | None | origin, automatically |
| Initial branch | The one in init.defaultBranch, not existing yet |
The source's default branch, already created |
| Working tree | Whatever you already had | Populated with the source's files |
| Needs a network | No | Yes (except for a local path) |
| One-line summary | "I am starting a project" | "I am joining a project" |
Ana used init because the project was born with her. Bruno uses clone because the project already existed. Every person on the team will run clone once per project and per machine; init is run exactly once in the life of a project.
graph TD
Q{"Does the project already exist<br/>in a Git repository?"}
Q -->|No| I["git init<br/>Empty history<br/>No remote"]
Q -->|Yes| C["git clone URL<br/>Complete history<br/>Remote 'origin' ready"]
I --> P["First commit"]
C --> W["Start working<br/>straight away"]
One frequent mistake deserves a mention of its own: running git init in an empty folder with the intention of "downloading" a project that already exists, and then trying to wire it up by hand. It is possible — add the remote and fetch, as we will see in module 4 — but it takes three steps where git clone takes one, and it is easy to end up misconfigured. If the project already exists, clone it.
- Checks to run after cloning
These four checks take ten seconds and prevent misunderstandings:
# 1. Where did this repository come from?
git remote -v
# → origin https://git.example.com/team/task-manager.git (fetch)
# → origin https://git.example.com/team/task-manager.git (push)
# 2. Which branch am I on?
git branch --show-current
# → main
# 3. Do I have the full history, or is this a shallow clone?
git rev-parse --is-shallow-repository
# → false
# 4. How many commits did I receive?
git rev-list --count HEAD
# → 4And a fifth one, which matters most on a team using conditional profiles (includeIf) like the one we set up in Configuring Git:
git config user.email
# → [email protected]Checking this before the first commit is far cheaper than fixing the authorship afterwards, which means rewriting the history.
Common Mistakes and Tips
- Cloning inside an existing repository. Run
git clonewithout paying attention while you are inside another project and you end up with a nested repository that confuses both Git and your colleagues. Check withpwdfirst, or withgit rev-parse --show-toplevel. - Using
--depth 1for your daily working copy. You save a few seconds once and loseblame,bisectand every historical comparison for months. Keep it for CI and containers. - Mixing up the SSH URL with the HTTPS one.
git@host:group/repo.gitis SSH (no://, with a colon);https://host/group/repo.gitis HTTPS. Copy the SSH one without having keys set up and the clone will fail withPermission denied (publickey). - Believing that cloning also copies uncommitted work. A clone brings only what has been committed. If something is missing, it is almost always because it was never committed.
- Expecting
.gitignoreto make files disappear when you clone. A clone reproduces what is in the history. If an unwanted file was committed back in the day, it will arrive all the same; ignoring it now does not take it out of the past. - Cloning a huge project over HTTPS on a slow network and assuming it has hung. Use
git clone --progress, or add--depthif you genuinely do not need the history. - Tip: if a clone fails halfway through, delete the half-created folder before retrying; otherwise the second attempt fails with
destination path already exists. - Tip: right after cloning, run
git log --oneline -5and skim the README. Two minutes there save you a round of questions to the team.
Exercises
Exercise 1: Simulate Bruno's clone with a local path
With no server and no network, reproduce the whole scenario. Starting from a repository with two commits standing in for Ana's:
- Create
~/practice/ana/task-managerwith aREADME.mdand anapp.js, and initialise it with two commits. - From it, create a bare repository at
~/practice/server/task-manager.gitto act as the central point. - Clone it into
~/practice/bruno/task-manager, playing the part of Bruno. - Prove with commands that Bruno has: the two commits, the
originremote pointing at the bare repository, and themainbranch trackingorigin/main.
Exercise 2: Compare a full clone with a shallow one
Using the bare repository from the previous exercise (extend it to five commits first so that the difference shows), make two clones: one full and one with --depth 1. Then:
- Compare the number of objects in each.
- Compare the output of
git log --oneline. - Try running
git logagainst a specific file in both and explain the difference. - Turn the shallow clone into a full one and check that it now behaves like the other.
Exercise 3: Pick the right command
For each situation, state the exact command you would use and briefly justify why. There is a deliberate trap in the list.
- Carla starts today and needs
task-manageron her Windows 11 machine, with the full history, for daily work. - The continuous integration server must build the latest version of
mainon every change, as fast as possible. - Ana wants to start a new project,
monthly-reports, which does not exist anywhere yet. - Bruno needs to look at what
styles.csswas like in the version taggedv1.0, without interrupting his work in his current copy. - The team is moving to a new server and the whole repository must go with it, branches and tags included.
- Carla already has a
task-managerfolder holding the project as downloaded in a.zipfrom a colleague, and wants to "connect" it to the team repository.
Solutions
Solution to Exercise 1
Step 1 — Ana's repository:
mkdir -p ~/practice/ana/task-manager
cd ~/practice/ana/task-manager
git init
echo "# Task Manager" > README.md
git add README.md
git commit -m "Add initial task manager structure"
echo "const tasks = [];" > app.js
git add app.js
git commit -m "Add skeleton of the task logic"
git log --onelineStep 2 — the central bare repository:
mkdir -p ~/practice/server
cd ~/practice/server
git clone --bare ~/practice/ana/task-manager task-manager.git--bare creates the replica with no working tree, which is exactly what a central repository should be:
Step 3 — Bruno's clone:
mkdir -p ~/practice/bruno
cd ~/practice/bruno
git clone ~/practice/server/task-manager.git
cd task-managerStep 4 — the three demonstrations:
# The two commits, with the same hashes as in Ana's repo
git log --oneline
# → 7d2f9a1 (HEAD -> main, origin/main, origin/HEAD) Add skeleton of the task logic
# → 1a4c8d6 Add initial task manager structure
# The origin remote points at the bare repository
git remote -v
# → origin /home/ana/practice/server/task-manager.git (fetch)
# → origin /home/ana/practice/server/task-manager.git (push)
# The main branch tracks origin/main
git status -sb
# → ## main...origin/main
git config branch.main.remote # → origin
git config branch.main.merge # → refs/heads/mainThe hashes matching Ana's repository exactly is no coincidence: as we saw in The Git Data Model, the hash derives from the content, so copying objects between repositories preserves the identifiers. That is the foundation that lets Git synchronise different machines without ambiguity.
Solution to Exercise 2
First we extend Ana's history and carry it over to the bare repository (the mechanics of pushing changes belong to module 4, so here we solve it by cloning again):
cd ~/practice/ana/task-manager
for n in 3 4 5; do echo "line $n" >> app.js; git commit -am "Change number $n"; done
rm -rf ~/practice/server/task-manager.git
git clone --bare . ~/practice/server/task-manager.gitThe two clones:
cd ~/practice
git clone ~/practice/server/task-manager.git full
git clone --depth 1 file://$HOME/practice/server/task-manager.git shallow(We use file:// because --depth has no effect on a direct local path: Git optimises that case by copying or linking the objects whole.)
1. Number of objects:
git -C full count-objects -v | grep in-pack
# → in-pack: 17
git -C shallow count-objects -v | grep in-pack
# → in-pack: 42. The history:
git -C full log --oneline
# → e9a2c5f (HEAD -> main, origin/main, origin/HEAD) Change number 5
# → b3d7f1c Change number 4
# → 5a8e2b9 Change number 3
# → 7d2f9a1 Add skeleton of the task logic
# → 1a4c8d6 Add initial task manager structure
git -C shallow log --oneline
# → e9a2c5f (grafted, HEAD -> main, origin/main) Change number 53. History of a single file:
git -C full log --oneline -- app.js
# → e9a2c5f Change number 5
# → b3d7f1c Change number 4
# → 5a8e2b9 Change number 3
# → 7d2f9a1 Add skeleton of the task logic
git -C shallow log --oneline -- app.js
# → e9a2c5f Change number 5The difference is not cosmetic: in the shallow clone the file appears to have sprung into existence, complete, in the last commit. Any investigation into when a line was introduced — git log, git blame or git bisect — will give a false answer. That is why --depth is for building, not for investigating.
4. Turning it into a full clone:
cd ~/practice/shallow
git rev-parse --is-shallow-repository # → true
git fetch --unshallow
git rev-parse --is-shallow-repository # → false
git log --oneline | wc -l # → 5grafted is gone and the history matches that of the full clone.
Solution to Exercise 3
1. Carla joins:
git clone [email protected]:team/task-manager.gitA plain, full clone. This is her daily working copy, so no --depth: she will need blame and the history. SSH if she has keys set up; HTTPS if she does not yet.
2. Continuous integration server:
This is the textbook case for a shallow clone: it builds and discards, the history adds nothing and the download is kept to a minimum. HTTPS with a token is the norm in CI because it avoids managing SSH keys.
3. A new project:
There is nothing to clone: the project is born here. git init <name> creates the folder into the bargain.
4. Inspecting version v1.0 without disturbing current work:
A second clone in another folder under another name leaves his working copy untouched. It will sit in detached HEAD, which is fine for reading. (A neater alternative he will meet in module 6: git worktree add.)
5. Server migration:
--mirror is the right option here, not plain --bare: it replicates every reference — branches, tags and remote references — and not just the local branches. It is the standard way to move a repository wholesale between servers.
6. The trap. Here you must not clone over the existing folder, and in fact Git would not let you: git clone demands an empty or non-existent directory. But the underlying problem is a different one: Carla's folder came from a .zip, so it has no history and no relationship whatsoever with the team repository. Wiring it up by hand would only breed conflicts and confusion.
The right answer is to set that folder aside and clone properly:
mv task-manager task-manager-zip-old # just in case
git clone [email protected]:team/task-manager.gitIf Carla had made changes in the .zip copy, she copies them by hand into the fresh clone and commits them there as ordinary changes. The general rule: a folder downloaded as a .zip is not a repository and git init does not turn it into one; to join an existing project, you clone.
Conclusion
Bruno is now part of the project. In this lesson we have seen that:
git clonedoes five things, not one: it creates the directory, initialises it, downloads the whole object database, registers the source as the remoteorigin, and creates the local branch, writing its files out to the working tree.- A clone brings the complete history, and that is the essence of the distributed model: Bruno can browse, compare and commit with no network.
- There are three common protocols: HTTPS (universal, with a token), SSH (comfortable day to day, with keys) and a local path (instant, for copies and testing). You can tell them apart at a glance from the shape of the URL.
- You can choose the directory name, the target branch with
--branchand how much history with--depth. - Shallow clones are excellent for CI and terrible for working, because they break
log,blameandbisect. The decision is reversible withgit fetch --unshallow. initandcloneanswer different questions:initwhen the project is born with you,clonewhen you join one that already exists.- A clone brings only what has been committed. Anything left uncommitted at the source stays behind.
Ana and Bruno each have their own repository now: one created hers, the other cloned his. From here on, both of them do exactly the same thing every single day: edit files, decide which changes belong together and record them in the history.
That cycle — edit, stage, commit — is Git's heartbeat and the subject of the next lesson, The Basic Git Workflow. There we will set in motion the three areas you have known since module 1, follow the complete life cycle of a file from the moment Git knows nothing about it until it is committed, and learn to read git status — in both its long and its short form — as the compass that tells you, at any moment, where you stand.
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
