Version Control Foundations and Distributed Collaboration: Guided Hands-On Workflow and Core Operations
Build a disposable Git repository step by step, observe working-tree and history state before and after each command, then exchange commits through a local bare repository and two independent clones.
Learning objectives
- Verify the Git client and use the help system before relying on memorized syntax.
- Initialize a repository with an explicit initial branch, configure safe local author metadata, and read status before changing state.
-
Explain what
git add,git commit,git diff, andgit logchange or inspect. - Create a local bare repository and understand why it is suitable as a collaboration endpoint.
- Clone, fetch, fast-forward, commit, and push while observing local branches and remote-tracking refs.
- Use inspection-before-mutation as the default workflow and choose a command from state rather than from habit.
1. Lab contract: disposable, explicit, observable
This lesson changes repository state repeatedly, so it begins with a rule: use a new disposable directory. Do not point these commands at a work project. Every important mutation is bracketed by inspection so you can see cause and effect.
<oid>, your Git output will show an object ID
appropriate to your repository's object format. Do not build
automation that assumes a fixed display length.
2. Verify the client and ask Git for help
git --version
git help -a
git help init
git help status
git --version establishes which client you are running.
git help -a lists available commands.
git help <command> opens the command manual using
your configured help viewer. In restricted environments where the
viewer is inconvenient, git <command> -h prints a
concise usage summary in the terminal.
3. Create the first repository with an explicit branch name
Use --initial-branch=trunk so the exercise behaves the
same regardless of your global
init.defaultBranch setting.
mkdir git-foundations-work
cd git-foundations-work
git init --initial-branch=trunk
git rev-parse --show-toplevel
git branch --show-current
git status --short --branch
Representative compact status:
## No commits yet on trunk
git init created repository metadata. It did not create
a commit. git status is therefore describing an
unborn branch: the branch name exists as the intended
current branch, but there is not yet a commit for it to point at.
4. Configure author identity locally, not globally, for this lab
git config --local user.name "Learner Example"
git config --local user.email "learner@example.invalid"
git config --local --get user.name
git config --local --get user.email
These values become commit metadata. They are not passwords and they
do not authenticate you to a remote. Using
--local keeps the exercise from changing your global
identity.
5. Working tree → staged snapshot → commit
Create a small file using your shell.
Git Bash, Bash, or zsh
printf "%s\n" "# Delivery Runbook" "" "Owner: platform-team" > runbook.md
PowerShell
@("# Delivery Runbook", "", "Owner: platform-team") | Set-Content -Encoding utf8 runbook.md
Now inspect before staging:
git status --short --branch
git diff -- runbook.md
Because the file is untracked, ordinary git diff does
not show it as a tracked-file patch. git status is the
important signal. Stage it, then inspect the staged comparison:
git add runbook.md
git status --short --branch
git diff --staged -- runbook.md
git add updates the index—the proposed content for the
next commit. It does not create a commit and it does not upload
anything. Now record the staged snapshot:
git commit -m "docs: add delivery runbook"
git status --short --branch
git log --oneline --decorate --graph -5
After the commit, the working tree and index match the recorded commit, so status should be clean.
6. Modify, compare, stage, and commit again
Git Bash, Bash, or zsh
printf "%s\n" "Preflight: verify staging health" >> runbook.md
PowerShell
"Preflight: verify staging health" | Add-Content -Encoding utf8 runbook.md
Before changing Git state, ask what changed:
git status --short
git diff -- runbook.md
The diff compares the working-tree version with the staged/index
version. After git add, ordinary
git diff becomes empty because the working tree and
index agree; git diff --staged then shows what the next
commit would contain.
git add runbook.md
git diff -- runbook.md
git diff --staged -- runbook.md
git commit -m "docs: add deployment preflight"
git log --oneline --decorate --graph -5
7. Map each command to the layer it reads or changes
| Command | Reads | Changes |
|---|---|---|
git status |
HEAD, index, working tree | Nothing substantive |
git diff |
Compared states | Nothing |
git add |
Working-tree content/pathspec | Index |
git commit |
Index + metadata | Object database and current branch ref |
git log |
Commit graph/refs | Nothing |
This table is intentionally simplified. Later chapters refine the index, objects, refs, and revision traversal. For now it gives you enough structure to predict the ordinary file lifecycle.
8. A bare repository: a collaboration endpoint without a normal working tree
Return to the parent directory and create a new bare repository. We keep the standalone repository above only as the first-workflow exercise; the collaboration exercise uses new repositories so the topology is obvious.
cd ..
git init --bare --initial-branch=trunk origin.git
git --git-dir=origin.git rev-parse --is-bare-repository
true
A bare repository stores Git repository data directly in the directory and does not have a normal checked-out working tree. Shared Git servers commonly use bare repositories because nobody should be editing a server-side working copy as part of normal collaboration.
9. Clone the empty origin, create history in Alice's clone, then push it
git clone origin.git alice
cd alice
git config user.name "Alice Example"
git config user.email "alice@example.invalid"
git status --short --branch
Cloning an empty repository may emit a warning that the repository
is empty. The clone still has a configured origin.
Create the first commit in Alice's clone:
Git Bash, Bash, or zsh
printf "%s\n" "# Shared Delivery Notes" "Owner: platform-team" > README.md
PowerShell
@("# Shared Delivery Notes", "Owner: platform-team") | Set-Content -Encoding utf8 README.md
git status --short --branch
git add README.md
git diff --staged -- README.md
git commit -m "docs: add shared delivery notes"
git log --oneline --decorate --graph --all -5
git push -u origin trunk
-u establishes upstream tracking for the local branch,
allowing later git push or status output to use that
relationship. Push transfers the necessary objects and asks the bare
repository to update its trunk ref.
10. Bob clones history, then Alice advances the shared repository
cd ..
git clone origin.git bob
cd bob
git config user.name "Bob Example"
git config user.email "bob@example.invalid"
git log --oneline --decorate --graph --all -5
cd ../alice
Alice adds another commit
Git Bash, Bash, or zsh
printf "%s\n" "Preflight: verify staging before deploy" >> README.md
PowerShell
"Preflight: verify staging before deploy" | Add-Content -Encoding utf8 README.md
git status --short
git diff -- README.md
git add README.md
git commit -m "docs: add staging verification note"
git push
At this point Bob's repository does not automatically know that Alice pushed a commit. That is the distributed model in action.
11. Fetch first, inspect remote-tracking state, then fast-forward deliberately
cd ../bob
git status --short --branch
git log --oneline --decorate --graph --all -5
git fetch origin
git log --oneline --decorate --graph --all -5
Before fetch, Bob's local trunk and his
local origin/trunk both reflect the old state. Fetch
contacts the other repository, downloads new objects if required,
and updates Bob's remote-tracking refs. It does
not automatically move Bob's local trunk.
Because Bob has made no local commit since cloning, the local branch can move forward without creating a merge commit:
git merge --ff-only origin/trunk
git status --short --branch
--ff-only refuses to invent a merge commit if a simple
pointer move is not possible. Full merge behavior belongs in Chapter
6; here we use the safest form needed to align Bob's branch with the
fetched shared history.
12. Bob commits, pushes, and Alice proves her clone is independent
Git Bash, Bash, or zsh
printf "%s\n" "Rollback owner: platform-team" > rollback.md
PowerShell
"Rollback owner: platform-team" | Set-Content -Encoding utf8 rollback.md
git status --short
git add rollback.md
git commit -m "docs: add rollback owner"
git push
cd ../alice
git log --oneline --decorate --graph --all -6
git fetch origin
git log --oneline --decorate --graph --all -6
git merge --ff-only origin/trunk
Before Alice fetches, her clone cannot see Bob's new commit. After
fetch, her origin/trunk advances while her local
trunk remains where it was until she integrates. That
sequence is the concrete proof that each clone maintains its own
repository state.
13. Mini challenge: choose from state, not from memory
You are in Bob's clone. A teammate says, “I pushed documentation updates.” You have uncommitted local edits. Which first command should you run?
A strong answer begins with git status, because your
local state determines what is safe. If the working state is
understood and fetching will not overwrite it,
git fetch origin is the next communication step. Do not
jump directly to a command that integrates or rewrites history
merely because “pull” sounds convenient.
14. Cleanup the disposable exercise
First verify you are about to remove only the disposable parent directory. Leave every repository, inspect its path, then remove the parent with your shell.
Git Bash, Bash, or zsh
cd ..
printf "Current directory: %s\n" "$PWD"
# Verify the disposable names before deletion:
ls
cd ..
rm -rf git-foundations-work origin.git alice bob
PowerShell
Set-Location ..
Get-Location
Get-ChildItem
Set-Location ..
Remove-Item -Recurse -Force git-foundations-work, origin.git, alice, bob
15. Knowledge check
Question 1. What changes when you run <code>git add runbook.md</code>?
Question 2. Why does <code>git fetch</code> not make Bob’s local branch automatically identical to <code>origin/trunk</code>?
Question 3. What does a successful <code>git merge --ff-only origin/trunk</code> mean in this lab?
Question 4. After Bob pushes, why can Alice’s <code>git log</code> remain unchanged until she fetches?
Question 5. Which command should normally come before a state-changing command when you are unsure of local edits?
git status is the primary first inspection. Depending
on the task, follow it with git diff,
git log, or ref/config inspection.
16. Summary
You have now seen the core loop rather than merely reading command definitions: inspect, modify a working file, stage the intended snapshot, inspect again, commit locally, and exchange commits explicitly through another repository. Clone created an independent repository. Fetch updated knowledge of another repository without silently moving the local branch. Push updated a remote ref only after a local commit existed.
Authoritative references
git-init
git-status
git-add
git-commit
git-diff
git-log
git-clone
git-fetch
git-push
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this
address.