Git and Linux Basics - Getting Started with Version Control
What You'll Learn
- Set up Git on Linux with the minimum viable configuration
- Master the core flow:
git init/add/commit/push - Avoid Linux-specific pitfalls (permissions, line endings, exec bit)
- Push to GitHub or GitLab via SSH key authentication
Quick Path (5 steps)
- Install:
sudo apt install git - Configure:
git config --global user.name / user.email - Initialize:
git initorgit clone <url> - Iterate:
git status→git add→git commit -m "..." - Share:
git remote add→git push -u origin main
Assumed Environment
- Ubuntu / Debian-based Linux (22.04 or later)
- bash or zsh
- GitHub / GitLab / self-hosted Git over SSH
1. Install and Configure
1-1. Install Git
$ sudo apt update $ sudo apt install -y git $ git --version
git version 2.43.0
RHEL / Fedora: sudo dnf install git. macOS: brew install git.
1-2. Set Global Configuration (one-time)
$ git config --global user.name "Your Name" $ git config --global user.email "you@example.com" $ git config --global init.defaultBranch main
init.defaultBranch main makes new repos use main automatically (the standard since GitHub adopted it in 2020).
1-3. Verify Configuration
$ git config --list --global
--global writes to ~/.gitconfig. To override per repo, run the same command without --global inside the repo (saved in .git/config).
2. Create or Clone a Repository
2-1. Initialize an Existing Directory
$ cd ~/projects/myapp $ git init $ ls -la .git
The directory is under Git control as soon as .git/ is created. You normally never need to edit it directly.
2-2. Clone a Remote
# HTTPS (simple, but requires PAT) $ git clone https://github.com/user/repo.git # SSH (recommended once keys are set up) $ git clone git@github.com:user/repo.git $ cd repo
GitHub deprecated HTTPS password authentication in 2021. You now need a Personal Access Token (PAT) for HTTPS. SSH is friction-free for daily use (see §6 for setup).
3. Core Flow: status → add → commit
Daily Git work is the loop "see changes → include them → snapshot."
3-1. Check the State
$ git status
On branch main
Untracked files:
(use "git add <file>..." to include in what will be committed)
README.md
3-2. Stage Changes
$ git add README.md # single file $ git add src/ # whole directory $ git add -p # interactive, hunk by hunk
git add . is convenient but easily picks up secrets like .env. Always run git status first.
3-3. Commit (Save the Snapshot)
$ git commit -m "Add README"
[main (root-commit) abc1234] Add README 1 file changed, 5 insertions(+) create mode 100644 README.md
Mental model
status= "what's the current state?"add= "include this in the next commit"commit= "freeze this snapshot"
4. View History: log / diff
4-1. Commit History
$ git log --oneline --graph --decorate -10
* abc1234 (HEAD -> main) Add README * def5678 Initial commit
A handy alias for daily use:
$ git config --global alias.lg "log --oneline --graph --decorate --all -20" $ git lg
4-2. Diffs
$ git diff # working tree vs stage $ git diff --staged # stage vs HEAD $ git diff HEAD~1 HEAD # previous commit vs current $ git diff main..feature # between branches
4-3. File-Level History
$ git log --follow -p src/index.js
--follow traces the file across renames.
5. Work with Remotes: push / pull
5-1. Add a Remote
$ git remote add origin git@github.com:user/repo.git $ git remote -v
origin git@github.com:user/repo.git (fetch) origin git@github.com:user/repo.git (push)
5-2. First Push
$ git push -u origin main
-u (--set-upstream) lets you omit origin main for subsequent git push / git pull commands.
5-3. pull / fetch
$ git pull # fetch + merge automatically $ git fetch # download without merging $ git log HEAD..origin/main # show what's ahead of you
Common pull accident in team work
git pullruns an automatic merge, so conflicts with local changes are easy to trigger.- When unsure, split into three steps:
git fetch→git log HEAD..origin/main→git merge. - For rebase workflow, consider
git config --global pull.rebase true.
6. Set Up SSH Key Authentication
6-1. Generate a Key Pair
$ ssh-keygen -t ed25519 -C "you@example.com" $ cat ~/.ssh/id_ed25519.pub
ed25519 is the modern default — fast and compact. Use -t rsa -b 4096 only for legacy systems that don't support ed25519.
Always set a passphrase to limit damage if the key leaks. Load it into ssh-agent so you don't type it every time.
6-2. Register the Public Key on GitHub / GitLab
Copy the contents of ~/.ssh/id_ed25519.pub (the .pub file) and add it under Settings → SSH and GPG keys. Never share the private key id_ed25519.
6-3. Test the Connection
$ ssh -T git@github.com
Hi user! You've successfully authenticated, but GitHub does not provide shell access.
This message means success (the exit code is 1, but that's expected).
7. .gitignore: Linux-Specific Patterns
# OS / editors
.DS_Store
*~
.*.swp
.idea/
.vscode/
# Language runtimes
node_modules/
__pycache__/
*.pyc
target/
build/
dist/
# Secrets (most important)
.env
.env.*
*.key
*.pem
# Logs
*.log
logs/
The official github/gitignore repo has language-specific templates. When in doubt, copy from there.
Already-tracked files are NOT excluded by .gitignore
If a file was committed before being added to .gitignore, Git keeps tracking it.
$ git rm --cached .env $ echo ".env" >> .gitignore $ git commit -m "Untrack .env"
Note: this only stops future tracking. The file remains in past commits. If a real secret leaked, you need history rewriting (git filter-repo) plus key rotation.
8. Common Linux-Specific Pitfalls
8-1. Permission Denied on Push
ERROR: Permission to user/repo.git denied to other-user. fatal: Could not read from remote repository.
Likely causes:
- The SSH key isn't registered on GitHub
- Multiple keys are loaded and the wrong account's key is used first
- You don't have write access (not a collaborator)
Diagnosis:
$ ssh -T git@github.com # which user are you authenticated as? $ ssh-add -l # list keys loaded in ssh-agent
For multiple accounts, use ~/.ssh/config to assign keys per host.
8-2. Mixed Line Endings (CRLF / LF)
Files coming from Windows can poison the diff (entire files appear changed).
# On Linux / macOS: keep line endings as-is (recommended) $ git config --global core.autocrlf input
To pin the rule per repo, add .gitattributes:
* text=auto eol=lf *.sh text eol=lf *.bat text eol=crlf
8-3. The Executable Bit (chmod +x) Isn't Recorded
$ chmod +x scripts/deploy.sh $ git status # may show no change
Force Git to record the mode change:
$ git update-index --chmod=+x scripts/deploy.sh $ git diff
old mode 100644 new mode 100755
If core.fileMode = false is set in the repo, Git ignores mode changes. Verify with git config core.fileMode.
8-4. Filename Case Differences
The Linux filesystem (ext4 etc.) is case-sensitive, but macOS and Windows default to case-insensitive. Mistaking Readme.md for README.md will break Linux-based CI.
# Detect case mismatches across the repo $ git config --global core.ignorecase false
A naming convention in CONTRIBUTING.md plus PR review is the realistic fix.
9. Safe Templates
First-time setup
git init echo "node_modules/" > .gitignore git add README.md .gitignore git commit -m "Initial commit" git remote add origin git@github.com:user/repo.git git push -u origin main
Daily loop
git status git diff git add -p # review and stage hunks git commit -m "feat: explain the change" git push
Undo operations (least to most destructive)
git restore <file> # discard working-tree changes git restore --staged <file> # unstage (file content kept) git commit --amend # fix the last commit (only before push) git revert <commit> # create an inverse commit (safe after sharing)
Never do this
git push --forceon a shared commit (rewrites everyone's history)git reset --hardwithout confirming (uncommitted work disappears)- Commit
.env, private keys, or auth tokens (very hard to remove from history) --no-verifyto skip hooks (silently swallows lint or test failures)