<!--order=2-->
<h1>Git: Staging and Tracking Files</h1>
<p>The staging area (also called the index) is what separates Git from simpler version control systems. You don't commit everything in your working directory — you choose exactly which changes to include. This gives you control over the shape of your commit history.</p>

<h2>Commands Covered</h2>
<p><code>git add</code> · <code>git rm</code> · <code>git mv</code> · <code>git restore</code> · <code>git ls-files</code> · <code>git check-attr</code></p>

<h2>git add</h2>
<p>Stages changes — moves content from the working tree into the staging area. Only staged changes are included in the next commit.</p>
<pre>
# Stage a specific file
$ git add README.md

# Stage multiple files
$ git add src/main.c src/utils.c

# Stage all changes in the current directory (and subdirectories)
$ git add .

# Stage all tracked files that were modified or deleted (not new untracked files)
$ git add -u
$ git add --update

# Interactive staging — choose which hunks (pieces) of files to stage
$ git add -p
$ git add --patch
# Git shows each changed section and asks: stage this hunk? (y/n/s/q/?)
#   y = yes, n = no, s = split into smaller hunks, q = quit, ? = help

# Stage everything including new untracked files
$ git add -A
$ git add --all

# Stage a specific directory
$ git add src/
</pre>

<h3>Partial Staging with -p</h3>
<p>The <code>-p</code> flag is one of Git's most useful features — it lets you stage part of a file's changes. This lets you make one commit for a bug fix and a separate commit for a refactor even if both changes are in the same file:</p>
<pre>
$ git add -p myfile.c
# Git shows each hunk and waits for input:
# Stage this hunk [y,n,q,a,d,s,e,?]?
#   y = stage this hunk
#   n = don't stage this hunk
#   s = split into smaller hunks
#   e = manually edit the hunk
#   q = quit (don't stage remaining hunks)
#   a = stage this and all remaining hunks in this file
#   d = don't stage this or any remaining hunks in this file
</pre>

<h2>git rm</h2>
<p>Removes files from both the working tree and the staging area. Unlike just deleting a file, <code>git rm</code> stages the deletion so it's included in the next commit.</p>
<pre>
# Remove a file from disk and stage the deletion
$ git rm oldfile.txt

# Remove from staging area only (keep the file on disk — "untrack" it)
$ git rm --cached secret.log
# Useful when you accidentally committed a file that should be in .gitignore

# Remove a directory recursively
$ git rm -r old-directory/

# Force removal even if the file has local modifications
$ git rm -f modified-file.txt
</pre>

<h2>git mv</h2>
<p>Renames or moves a file and stages the change. Equivalent to <code>mv old new && git rm old && git add new</code>, but in one step. Git tracks renames via content similarity — it will detect renames even if you use regular <code>mv</code>, but <code>git mv</code> is cleaner.</p>
<pre>
# Rename a file
$ git mv old-name.txt new-name.txt

# Move a file into a subdirectory
$ git mv README.md docs/README.md

# Rename a directory
$ git mv old-dir/ new-dir/
</pre>

<h2>git restore</h2>
<p>Restores files in the working tree or the staging area. Introduced in Git 2.23 as a clearer alternative to <code>git checkout -- &lt;file&gt;</code>.</p>
<pre>
# Discard working tree changes (revert a file to its staged/committed state)
$ git restore myfile.c
# WARNING: this discards unsaved changes — they cannot be recovered

# Unstage a file (move it from staging back to working tree, keep changes)
$ git restore --staged myfile.c

# Restore a file to its state in a specific commit
$ git restore --source=HEAD~3 myfile.c
$ git restore --source=abc1234 myfile.c

# Restore all files in a directory to their committed state
$ git restore .

# Restore both the staging area and working tree to HEAD
$ git restore --staged --worktree myfile.c
</pre>

<h2>git ls-files</h2>
<p>Shows information about files in the index and working tree. Useful for scripting and understanding what Git is tracking.</p>
<pre>
# List all tracked files
$ git ls-files

# List untracked files
$ git ls-files --others
$ git ls-files -o

# List ignored files
$ git ls-files --ignored --exclude-standard
$ git ls-files -i --exclude-standard

# List files with their status in the index
$ git ls-files --stage
# Output: mode SHA1 stage_number  filename
#   mode:   100644 = regular file, 100755 = executable, 120000 = symlink
#   stage:  0 = normal, 1/2/3 = merge conflict stages

# List only modified files (differ between index and working tree)
$ git ls-files --modified

# List deleted files
$ git ls-files --deleted

# List files matching a pattern
$ git ls-files '*.c'
</pre>

<h2>git check-attr</h2>
<p>Shows the Git attributes assigned to files via <code>.gitattributes</code>. Attributes control things like line endings, diff behaviour, and merge strategies per file type.</p>
<pre>
# Check a specific attribute for a file
$ git check-attr text myfile.txt

# Check all attributes for a file
$ git check-attr -a myfile.txt

# Check attributes for multiple files
$ git check-attr text -- *.txt *.md

# Example .gitattributes file:
# *.txt   text=auto
# *.sh    eol=lf
# *.png   binary
# *.md    diff=markdown
</pre>

<h2>The .gitignore File</h2>
<p>Files matching patterns in <code>.gitignore</code> are not tracked or shown as untracked. Ignored files are excluded from <code>git add .</code>:</p>
<pre>
# Example .gitignore
*.log           # ignore all .log files
build/          # ignore the build directory
!build/keep.txt # but not this specific file (! = exception)
*.o             # compiled object files
.env            # environment variables (never commit secrets)
node_modules/   # npm dependencies
__pycache__/    # Python bytecode
.DS_Store       # macOS metadata files

# Create a global gitignore (applies to all your repos)
$ git config --global core.excludesFile ~/.gitignore_global
</pre>

<h2>Workflow Summary</h2>
<pre>
# Typical workflow:
$ git status              # see what's changed
$ git add -p              # selectively stage hunks
$ git status              # confirm what's staged
$ git diff --staged       # review what will be committed
$ git commit -m "message" # commit staged changes
</pre>

<h2>References</h2>
<ul>
  <li><a href="https://git-scm.com/docs/git-add" target="_blank" rel="noopener">git-add documentation ↗</a></li>
  <li><a href="https://git-scm.com/docs/git-restore" target="_blank" rel="noopener">git-restore documentation ↗</a></li>
  <li><a href="https://git-scm.com/docs/gitignore" target="_blank" rel="noopener">gitignore documentation ↗</a></li>
</ul>
