Commit fc3e0ab6 authored by Matt Belhorn's avatar Matt Belhorn
Browse files

Adds slides and demo material for staging talk.

parent 52ad7c21
#!/usr/env python3
'''
This is a demo of logically separate changes
made all at once, but committed separately.
'''
def square(number):
'''Returns square of number'''
return number**2
def half(number):
'''Returns integer half of number'''
return number//2
def main():
'''What runs when the script is called.'''
output = square(5)
output += half(34)
print(output)
if __name__ == '__main__':
main()
I will be deleted.
But I came back from the grave.
<!--
$theme: default
template: gaia
page_number: true
$size: 16:9
-->
# Staging and the Git Index
Understanding what gets Committed
---
# Version Control: A History of Changes
As a version control system, Git keeps a history of changes made to files in a given **working tree**. This '*git history*' grows when changes are *committed* to the history.
However, not all changes made in a working tree need be comitted to history.
*The history reflects only the changes we select for a given commit.*
---
# Meet the Git Index
The **git index** is the *collection of tracked file contents* in a given **working tree** of a git repository. It lives at `$TOPLEVEL/.git/index`
It represents the recorded history of previous commits plus the changes to be included in the next commit.
The index can be thought of as the table of contents of a working tree as seen by git.
---
# Let's explore the index with a simple repo
```text
$ git init .
Initialized empty Git repository in /tmp/demo/.git
$ git rev-parse --show-toplevel
/tmp/demo
```
Notice that an index doesn't exist if no content in the working tree has been explicitly tracked.
```text
$ echo "Hello, index" > hello.txt
$ ls .git/index
ls: cannot access '.git/index': No such file or directory
```
---
# Got any change? Find out with status
To find out which files have been changed in the working tree since the last commit,
use `git status`:
```text
$ git status
On branch master
Initial commit
Untracked files:
(use "git add <file>..." to include in what will be committed)
hello.txt
nothing added to commit but untracked files present (use "git add" to track)
```
As the message states, we must explicitly add the changes we want to index for the next commit.
---
# Staging the index for the next commit
In addition to the suggestion of `git status`, there are several other options for altering content tracked in the index. We'll look at some of these closer.
- `git add [--patch]`
- `git rm [--cached]`
- `git mv` (`git rm; git add`)
- `git checkout {branch} -- {file}`
---
# New files
Whole files can be tracked in their current state by using `git add`.
```text
$ git add hello.txt
$ git status
On branch master
Initial commit
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: hello.txt
```
---
# Make History
The `git commit` command commits the current state of the index to the git history and updates the branch's `HEAD` ref to point to the new commit.
```text
$ git commit -m "Initial commit"
[master (root-commit) 5df9475] Initial commit
1 file changed, 1 insertion(+)
create mode 100644 hello.txt
```
When all the changes to *already tracked* files in a working tree should be added to the next commit, it is often easiest to add them at commit time with
`git commit -a`
This will not add untracked files to the commit.
---
Changes made to a previously tracked file will appear in status output as `modified`, rather than `new file`.
```text
$ echo "I see you've changed." >> hello.txt
$ git status
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: hello.txt
no changes added to commit (use "git add" and/or "git commit -a")
```
---
```text
$ git add hello.txt
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: hello.txt
$ git commit -m "Changed hello.txt"
[master e54f97b] Changed hello.txt
1 file changed, 1 insertion(+)
```
For the record, that is an awful commit message. We know both that files changed and which ones from the commit itself. The message might as well have been blank. A better message might have read
> Adds filler text to demonstrate changes to a tracked file.
---
# Adding partial changes
Git commits are most useful when they each represent an atomic change to your work. Often times we make a number of separate logically unique changes all at once that might be better if added in separate commits.
We can do this with `git add --patch`.
Suppose we start with the following file `demp.py` already committed:
```python3
#!/usr/env python3
def main():
pass
if __name__ == '__main__':
main()
```
---
And change it to
```
$ cat demo.py
#!/usr/env python3
'''
This is a demo of logically separate changes
made all at once, but committed separately.
'''
def square(number):
'''Returns square of number'''
return number**2
def half(number):
'''Returns integer half of number'''
return number//2
def main():
'''What runs when the script is called.'''
output = square(5)
output += half(34)
print(output)
if __name__ == '__main__':
main()
```
---
Using `git add --patch`, we can select parts of the file to commit first:
---
```
$ git diff --cached
diff --git a/demo.py b/demo.py
index dc0e616..26540fc 100644
--- a/demo.py
+++ b/demo.py
@@ -1,7 +1,18 @@
#!/usr/env python3
+'''
+This is a demo of logically separate changes
+made all at once, but committed separately.
+'''
+
+def square(number):
+ '''Returns square of number'''
+ return number**2
+
def main():
- pass
+ '''What runs when the script is called.'''
+ output = square(5)
+ print(output)
if __name__ == '__main__':
main()
```
---
```text
$ git commit -m "Adds docstrings and a square function."
[master a8da270] Adds docstrings and a square function.
1 file changed, 12 insertions(+), 1 deletion(-)
```
```text
$ git add demo.py; git diff --cached
diff --git a/demo.py b/demo.py
index 26540fc..3c2ec13 100644
--- a/demo.py
+++ b/demo.py
@@ -8,10 +8,14 @@ def square(number):
'''Returns square of number'''
return number**2
+def half(number):
+ '''Returns integer half of number'''
+ return number//2
def main():
'''What runs when the script is called.'''
output = square(5)
+ output += half(34)
print(output)
if __name__ == '__main__':
```
---
<!-- footer: Note: my `ls` is a custom alias for `log --pretty=format:'%C(yellow)%h%Cred | %Creset%s%Cblue [%cn]%Cred%d' --decorate` -->
```text
$ git commit -m "Adds half function to get the ultimate answer."
[master fdcb441] Adds half function to get the ultimate answer.
1 file changed, 4 insertions(+)
```
Now we have the changes recorded in logically consistent separate commits.
```text
$ git ls -2
fdcb441 | Adds half function to get the ultimate answer. [Matt Belhorn] (HEAD -> master)
a8da270 | Adds docstrings and a square function. [Matt Belhorn]
```
---
<!-- footer: -->
# Moving, deleting, untracking
Once a file is tracked, `git rm` can be used to untrack it and delete the file from the working tree. To simply untrack a file (and not delete it from the working tree) use `git rm --cached`.
Files that were previously committed can be checked out from the earlier commit, even if they are deleted from the working tree or the index at some point.
Using `/bin/rm` will remove a file from the working tree, but not the index!
Likewise, `git mv` will change the name of a file in both the working tree and the index. Using `/bin/mv` alone will not alter the index. Using `git mv a b`, if you can remember to, is easier than `mv a b; git rm a; git add b` and generally safer than `git add -A`.
Checking `git status` will indicate when a file has been moved or renamed on disk but not in the index.
---
# Sometimes we're afraid of change
Should you ever want the state of a file in the working tree to be exactly as it was in a previous commit (ie, discard all changes), there are a couple of options.
- `git checkout {commit} -- {file}`: This will return `{file}` to the state it had at `{commit}`. Oftentimes, the commit we want is the last `HEAD` but you an checkout single files from other branches too.
- `git reset [--hard|--mixed|--soft|--merge|--keep] {commit}`: A nuclear option. This will move the `HEAD` to the given commit. What happens the index or working tree files depends on the option.
- `--hard` resets both: changes will be lost.
- `--mixed` (default) resets the index, but leaves files in the working tree as-is.
- `--soft` changes only the commit to which `HEAD` points and nothing else.
- `--keep` and `--merge` are advanced. See the truth tables in the man page.
---
# Show me what you got
To see what files are in the index, use `git ls-files`. These are all of the files tracked on the current branch's index.
To see specific file changes from the working tree and last commit `HEAD`, use `git diff`.
To see specific file changes from the index and last commit `HEAD`, use `git diff --cached`.
---
# Ignoring Specific files
Sometimes we don't want git to notice and attempt to track things like log files, compiled code, or files with secret contents.
Rather than carefully ignore these files, we can instruct git to ignore them. In any directory, you can place a `.gitignore` file containing file path patterns that will be ignored in all subdirectories.
A global `.gitignore` can also be set at one of `$HOME/.gitignore`, `$HOME/.config/git/ignore`, or `$TOPLEVEL/.git/info/exclude`. Interestingly, using the repo's `exclude` file will ignore paths without requiring a new commit.
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment