Learn how git represents commits and branches internally to gain deep insights into how git works under the hood…
了解git如何在内部表示提交和分支,从而深入了解git的底层工作原理……
Discover the power of rebasing in git, along with how to avoid common pitfalls. Learn to tell when it’s appropriate to re-write history…
了解在git中重新建立基础的强大功能,以及如何避免常见的陷阱。学会分辨什么时候改写历史是合适的……
Discover how git hooks can make your life easier by running analysis on your code before you commit in order to prevent common errors from making their way into your codebase…
❯ tree .git .git ├── HEAD ├── config ├── description ├── info │ └── exclude ├── objects # our blob is stored in objects │ ├──8a # the directory starts with the first 2 chars of the hash. │ │ └── b686eafeb1f44702738c8b0f24f2567c36da6d │ ├── info │ └── pack └── refs ├── heads └── tags
The files in your working area that are also not in the staging are are not handled by git.
Also called untracked files
THE STAGING AREA (A.K.A INDEX, CACHE)
What files are going to be part of the next commit
The staging area is how git knows what will change between the current commit and the next commit.
THE REPOSITORY
The files git knows about!
Contains all of your commits
CLOSER LOOK: THE STAGING AREA
THE STAGING AREA
The staging area is how git knows what will change between the current commit and the next commit.
Tip: a “clean” staging area isn’t empty!
Consider the baseline staging area as being an exact copy of the latest commit.
1 2 3 4
❯ git ls-files -s # The plumbing command ls-files -s will show you what’s in the staging area. 100644 b8b2583b242add97d513d8d8b85a46b9b5fb29cb 0 index.txt 100644 ed52afd72f64b1f8575e81bb4cb02ef1215739f6 0 posts/ welcome.txt
MOVING FILES IN & OUT OF THE STAGING AREA
add a file to the next commit:
git add <file>
delete a file in the next commit:
git rm <file>
rename a file in the next commit:
git mv <file>
GIT ADD -P
one of my favorite tools
allows you to stage commits in hunks
interactively!
It’s especially useful if you’ve done too much work for one commit.
❯ git add -p . . . diff displayed . . . Stage this hunk [y,n,q,a,d,/,j,J,g,e,?]? ? y - stage this hunk n - do not stage this hunk q - quit; do not stage this hunk or any of the remaining ones a - stage this hunk and all later hunks in the file d - do not stage this hunk or any of the later hunks in the file g - select a hunk to go to / - search for a hunk matching the given regex j - leave this hunk undecided, see next undecided hunk J - leave this hunk undecided, see next hunk k - leave this hunk undecided, see previous undecided hunk K - leave this hunk undecided, see previous hunk s - split the current hunk into smaller hunks e - manually edit the current hunk ? - printhelp
“UNSTAGE” FILES FROM THE STAGING AREA
Not removing the files.
You’re replacing them with a copy that’s currently in the repository.
BASICS: HOW CONTENT MOVES IN GIT
Stashing
GIT STASH
Save un-committed work.
The stash is safe from destructive operations.
GIT STASH - BASIC USE
stash changes
git stash
list changes
git stash list
show the contents
git stash show stash@{0}
apply the last stash
git stash apply
apply a specific stash
git stash apply stash@{0}
ADVANCED STASHING - KEEPING FILES
Keep untracked files
git stash --include-untracked
Keep all files (even ignored ones!)
git stash --all
ADVANCED STASHING - OPERATIONS
Name stashes for easy reference
git stash save "WIP: making progress on foo"
Start a new branch from a stash
git stash branch <optional stash name>
Grab a single file from a stash
git checkout <stash name> -- <filename>
ADVANCED STASHING - CLEANING THE STASH
Remove the last stash and applying changes:
git stash pop
tip: doesn’t remove if there’s a merge conflict
Remove the last stash
git stash drop
Remove the nth stash
git stash drop stash@{n}
Remove all stashes
git stash clear
EXAMINING STASH CONTENTS - GIT SHOW
1 2 3 4 5 6 7 8 9 10
❯ git stash list stash@{0}: WIP on master: 9703ef3 example stash@{1}: WIP on example3: 0be5e31 hello stash@{2}: WIP on example3: 0be5e31 a commit
echo"This is a test of the emergency git-casting system" >> hello.txt
git add -p
diff --git a/hello.txt b/hello.txt index 980a0d5..424b148 100644 --- a/hello.txt +++ b/hello.txt @@ -1 +1,2 @@ Hello World! +This is a test of the emergency git-casting system Stage this hunk [y,n,q,a,d,e,?]? # press Enter
y - stage this hunk n - do not stage this hunk q - quit; do not stage this hunk or any of the remaining ones a - stage this hunk and all later hunks in the file d - do not stage this hunk or any of the later hunks in the file e - manually edit the current hunk ? - printhelp @@ -1 +1,2 @@ Hello World! +This is a test of the emergency git-casting system Stage this hunk [y,n,q,a,d,e,?]? y # press y and enter to stage this hunk
git status On branch exercise2 Your branch is up to date with 'origin/exercise2'.
Changes to be committed: (use "git reset HEAD <file>..." to unstage)
On branch exercise2 Your branch is up to date with 'origin/exercise2'.
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")
Stash your change with a unique stash message, then unstash and apply it back to the exercise2 branch.
Saved working directory and index state On exercise2: emergency git-casting
git status
On branch exercise2 Your branch is up to date with 'origin/exercise2'.
nothing to commit, working tree clean
git stash list
stash@{0}: On exercise2: emergency git-casting
git stash show stash@{0}
hello.txt | 1 + 1 file changed, 1 insertion(+)
git stash apply stash@{0}
On branch exercise2 Your branch is up to date with 'origin/exercise2'.
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")
git diff hello.txt
diff --git a/hello.txt b/hello.txt index 980a0d5..424b148 100644 --- a/hello.txt +++ b/hello.txt @@ -1 +1,2 @@ Hello World! +This is a test of the emergency git-casting system
References, Commits, Branches
References(pointers to commits)
THREE TYPES OF GIT REFERENCES
Tags & Annotated Tags
Branches
HEAD
WHAT’S A BRANCH
A branch is just a pointer to a particular commit.
The pointer of the current branch changes as new commits are made.
WHAT IS HEAD
HEAD is how git knows what branch you’re currently on, and what the next parent will be.
It’s a pointer.
It usually points at the name of the current branch.
But, it can point at a commit too (detached HEAD).
It moves when:
You make a commit in the currently active branch
When you checkout a new branch
1 2 3 4 5 6 7 8 9 10
❯ git checkout master
❯ cat .git/HEAD ref: refs/heads/master
❯ git checkout feature Switched to branch 'feature'
❯ cat .git/HEAD ref: refs/heads/feature
SAMPLE REPO - A SIMPLE BLOG
We’re working on a simple blog.
index.txt contains links to our entries, and post titles
the posts/ directory contains our blog entries.
1 2 3
❯ mkdir posts ❯ echo'This is my very first blog entry.'> posts/welcome.txt ❯ echo'welcome.txt Welcome to my blog' > index.txt
❯ git checkout -b tech_posts Switched to a new branch 'tech_posts'
1 2 3 4 5
❯ git add posts/python.txt ❯ git commit -m "New blog post about python" [tech_posts 2b0b3f2] New blog post about python 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 posts/python.txt
Tags & Annotated Tags
LIGHTWEIGHT TAGS
Lightweight tags are just a simple pointer to a commit.
When you create a tag with no arguments, it captures the value in HEAD
1 2 3
❯ git checkout master Switched to branch 'master' ❯ git tag my-first-commit
ANNOTATED TAGS: GIT TAG -A
Point to a commit, but store additional information.
Author, message, date.
1 2 3 4 5 6 7
❯ git tag -a v1.0 -m "Version 1.0 of my blog" ❯ git tag my-first-commit v1.0 ❯ git show v1.0 tag v1.0 Tagger: Nina Zakharenko <nina@nnja.io> Date: Sun Sep 24 17:01:21 2017 -0700 Version 1.0 of my blog
WORKING WITH TAGS
List all the tags in a repo
git tag
List all tags, and what commit they’re pointing to
git show-ref --tags
List all the tags pointing at a commit
git tag --points-at <commit>
Looking at the tag, or tagged contents:
git show <tag-name>
TAGS & BRANCHES
Branch
The current branch pointer moves with every commit to the repository
Tag
The commit that a tag points doesn’t change. It’s a snapshot!
Detached Head & Dangling Commits
HEAD-LESS / DETACHED HEAD
Sometimes you need to checkout a specific commit (or tag) instead of a branch.
git moves the HEAD pointer to that commit
as soon as you checkout a different branch or commit, the value of HEAD will point to the new SHA
There is no reference pointing to the commits you made in a detached state.
1 2 3 4 5 6 7 8 9
❯ git checkout cd0b57 Note: checking out 'cd0b57'.
You are in'detached HEAD' state. You can look around, make experimental changes and commit them, and you can discard any commits you make in this state without impacting any branches by performing another checkout.
1 2 3 4 5
❯ git add posts/second-post.txt ❯ git commit -m "My second post" [detached HEAD 1f6ee83] My second post 1 file changed, 1 insertion(+) create mode 100644 posts/second-post.txt
Save your work:
Create a new branch that points to the last commit you made in a detached state.
git branch <new-branch-name> <commit>
Why the last commit?
Because the other commits point to their parents.
DANGLING COMMITS
Discard your work:
If you don’t point a new branch at those commits, they will no longer be referenced in git. (dangling commits)
Eventually, they will be garbage collected.
1 2 3 4
❯ git checkout master Warning: you are leaving 1 commit behind, not connected to any of your branches: 1f6ee83 My second post
diff --git a/hello.txt b/hello.txt index 980a0d5..b31a35b 100644 --- a/hello.txt +++ b/hello.txt @@ -1 +1,2 @@ Hello World! +This is a test of the emergency git-casting system.
Get into a “detached HEAD” state by checking out a specific commit, then confirm that your HEAD is pointing at this commit rather than at a branch.
You are in'detached HEAD' state. You can look around, make experimental changes and commit them, and you can discard any commits you make in this state without impacting any branches by performing another checkout.
If you want to create a new branch to retain commits you create, you may do so (now or later) by using -b with the checkout command again. Example:
git checkout -b <new-branch-name>
HEAD is now at e348ebc Testing the emergency git-casting system
cat .git/HEAD
e348ebc1187cb3b4066b1e9432a614b464bf9d07
Make a new commit, then switch branches to confirm that you’re leaving a commit behind.
Fast-forward happens when there are no commits on the base branch that occurred after the feature branch was created.
GIT MERGE –NO-FF (NO FAST FORWARD)
To retain the history of a merge commit, even if there are no changes to the base branch:
use git merge --no-ff
This will force a merge commit, even when one isn’t necessary.
1 2 3
git checkout master
git merge new_feature --no-ff
Merge Conflicts
Attempt to merge, but files have diverged.
Git stops until the conflicts are resolved.
1 2 3 4
git merge feature
Auto-merging feature CONFLICT (add/add): Merge conflict in feature
GIT RERERE - REUSE RECORDED RESOLUTION
git saves how you resolved a conflict
next conflict: reuse the same resolution
useful for:
long lived feature branch (like a refactor)
rebasing
Turn it on:
git config rerere.enabled true
use –global flag to enable for all projects
1 2 3 4 5 6 7 8 9 10 11 12
❯ git config rerere.enabled true ❯ git checkout master ❯ git merge feature Auto-merging feature CONFLICT (add/add): Merge conflict in file Recorded preimage for'file' Automatic merge failed; fix conflicts and then commit the result. ### Fix the merge conflict in file. ❯ git add file ❯ git commit -m "Resolve conflict" Recorded resolution for'feature'. [master 0fe266d] Resolve conflict
When I commit the conflict resolution, it’s recorded.
Now: Feature wasn’t ready, I undid the merge.
When I try to merge again:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
❯ git merge feature Auto-merging feature CONFLICT (add/add): Merge conflict in feature Resolved 'feature' using previous resolution. Automatic merge failed; fix conflicts and then commit the result.
❯ git add file
❯ git diff --staged diff --git a/file b/file index 587be6b..a354eda 100644 --- a/file +++ b/file @@ -1 +1 @@ -The old change +This is how I resolved my conflict. \ No newline at end of file
Use git reset –hard to reset your exercise4 branch back one commit, then use the –no-ff option to git merge to merge exercise3 again. Look at the git log, how is it different from the last step?
git merge mundo Auto-merging hello.txt CONFLICT (content): Merge conflict in hello.txt Recorded preimage for'hello.txt' Automatic merge failed; fix conflicts and then commit the result.
git rerere diff --- a/hello.txt +++ b/hello.txt @@ -1,6 +1,6 @@ -<<<<<<< -Hello Mundo! -======= +<<<<<<< HEAD Hola World! ->>>>>>> +======= +Hello Mundo! +>>>>>>> mundo This is a test of the emergency git-casting system. git add hello.txt git commit -m "Merging in mundo branch" [exercise4 ff91b70] Merging in mundo branch
Backup again with git reset –hard, then attempt the merge again. Notice how ReReRe automatically resolves the conflict for you.
1 2 3 4 5 6 7 8 9 10 11 12
git reset --hard HEAD^ HEAD is now at fff6b44 Merge branch 'exercise3' into exercise4
git merge mundo Auto-merging hello.txt CONFLICT (content): Merge conflict in hello.txt Resolved 'hello.txt' using previous resolution. Automatic merge failed; fix conflicts and then commit the result.
cat hello.txt Hola Mundo! This is a test of the emergency git-casting system.
History & Diffs
Useful Commit Message
BAD COMMIT MESSAGES
GOOD COMMITS ARE IMPORTANT
Good commits help preserve the history of a code base.
Commit message is in future tense. ‘Fix’ vs ‘Fixed’ Short subject, followed by a blank line.
1 2 3 4 5 6 7
git-rebase: don't ignore unexpected command line arguments Currently, git-rebase will silently ignore any unexpected command-line switches and arguments (the command-line produced by git rev-parse). This allowed the rev-parse bug, fixed in the preceding commits, to go unnoticed. Let's make sure that doesn't happen again. We shouldn't be ignoring unexpected arguments. Let's not.
A description of the current behavior, a short summary of why the fix is needed. Mention side effects. The description is broken into 72 char lines.
ANATOMY OF A GOOD COMMIT
Good commit message
Encapsulates one logical idea
Doesn’t introduce breaking changes
i.e. tests pass
Git Log
git log - the basic command that shows you the history of your repository
vi hello.txt # Change your `hello.txt` to say [greeting] [noun]!
cat hello.txt [greeting] [noun]! This is a test of the emergency git-casting system.
git mv hello.txt hello.template # Rename hello.txt to hello.template
git add hello.template
git commit # This will open your text editor # Type the following... Replacing greeting with tokens for i18n
Currently, hello.txt contains both Spanish and English. Let's replace Hola with a [greeting] token, and Mundo with a [noun] token. That way, we can localize hello.txt for any language! # Save and exit your editor [exercise5 4b2b90e] Replacing greeting with tokens for i18n 1 file changed, 1 insertion(+), 1 deletion(-)
Use git log to find commits created since yesterday. Rename a file and use the --name-status and --follow options to git log to track down when the file was renamed, and what it used to be called. Use --grep to search within commit messages, and –diff-filter to find renamed and modified files from git log.
Currently, hello.txt contains both Spanish and English. Let\'s replace Hola with a [greeting] token, and Mundo with a [noun] token. That way, we can localize hello.txt for any language! commit 268a5e7a09a42973a84d93162c9022ccda45ad02 Author: liuyong <564447023@qq.com> Date: Thu Jul 18 14:28:00 2019 +0800 rename file git log --name-status --follow --oneline hello.template 145d1ac (HEAD -> exercise5) Replacing greeting with tokens for i18n M hello.template 268a5e7 rename file R073 hello.txt hello.template fec9e7b Changing Hello to Hola M hello.txt afa34a6 Changing World to Mundo M hello.txt e348ebc (tag: my-exercise3-tag, tag: exercise3-annotated-tag, origin/exercise3, exercise3) Testing the emergency git-casting system M hello.txt 43388fe (origin/master, origin/exercise7, origin/exercise4, origin/exercise2, origin/HEAD, master, exercise2) Initial commit A hello.txt git log --diff-filter=R --find-renames commit 268a5e7a09a42973a84d93162c9022ccda45ad02 Author: liuyong <564447023@qq.com> Date: Thu Jul 18 14:28:00 2019 +0800 rename file git log --diff-filter=M --oneline 145d1ac (HEAD -> exercise5) Replacing greeting with tokens for i18n fec9e7b Changing Hello to Hola afa34a6 Changing World to Mundo e348ebc (tag: my-exercise3-tag, tag: exercise3-annotated-tag, origin/exercise3, exercise3) Testing the emergency git-casting system
Use git show to get more information about a specific git hash.
Currently, hello.txt contains both Spanish and English. Let\'s replace Hola with a [greeting] token, and Mundo with a [noun] token. That way, we can localize hello.txt for any language! diff --git a/hello.template b/hello.template index a6c57ac..03686d4 100644 --- a/hello.template +++ b/hello.template @@ -1,2 +1,2 @@ [greeting] [noun]! -This is a test of the emergency git-casting system. + git show 145d1ac --stat --oneline 145d1ac (HEAD -> exercise5) Replacing greeting with tokens for i18n hello.template | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
Try the --merged and --no-merged options to git branch to see which branches have been merged into master (or not).
1 2 3 4 5 6 7 8 9
git branch --merged master exercise2 master
git branch --no-merged master exercise3 exercise4 * exercise5 mundo
Fixing Mistakes
Git Checkout
Restore working tree files or switch branches
WHAT HAPPENS WHEN YOU GIT CHECKOUT A BRANCH
Change HEAD to point to the new branch
Copy the commit snapshot to the staging area
Update the working area with the branch contents
WHAT HAPPENS WHEN YOU GIT CHECKOUT - - FILE
Replace the working area copy with the version from the current staging area
Warning: This operation overwrites files in the working directory without warning!
GIT CHECKOUT: OVERWRITE FILES WITH STAGING AREA COPY
Overwrite the working area file with the staging area version from the last commit
git checkout -- <file_path>
Warning: This operation overwrites files in the working directory without warning!
WHAT HAPPENS WHEN YOU GIT CHECKOUT - - FILE
Update the staging area to match the commit
Update the working area to match the staging area.
Warning: This operation overwrites files in the working directory without warning!
GIT CHECKOUT: FROM A SPECIFIC COMMIT
Checkout a file from a specific commit
copies to both working area & staging area
git checkout <commit> -- <file_path>
Restore a deleted file
git checkout <deleting_commit>^ -- <file_path>
Warning: This operation overwrites files in the working directory without warning!
Git Clean & Reset
GIT CLEAN
Git clean will clear your working area by deleting untracked files.
Warning: this operation cannot be undone!
Use the —dry-run flag to see what would be deleted
The -f flag to do the deletion
The -d flag will clean directories
1 2 3 4 5 6 7 8 9 10 11 12 13
❯ git clean --dry-run Would remove a-note.txt
❯ git clean -d --dry-run Would remove a-note.txt Would remove scratch/
Warning: never push changed history to a shared or public repository!
GIT RESET –
1
❯ git reset <file>
GIT RESET – : RESULT
GIT RESET –
1
❯ git reset <commit> -- <file>
GIT RESET – : RESULT
GIT RESET – CHEAT CHEAT
Move HEAD and current branch
Reset the staging area
Reset the working area
This operation does not work with flags!
UNDO A GIT RESET WITH ORIG_HEAD
In case of an accidental git reset -
Git keeps the previous value of HEAD in variable called ORIG_HEAD
To go back to the way things were:
git reset ORIG_HEAD
Git Revert
GIT REVERT - THE “SAFE” RESET
Git revert creates a new commit that introduces the opposite changes from the specified commit.
The original commit stays in the repository.
Tip:
Use revert if you’re undoing a commit that has already been shared.
Revert does not change history.
GIT REVERT - PICK A COMMIT TO UNDO
1 2 3 4 5 6 7 8 9 10
❯ git log --oneline 2b0b3f2 (HEAD -> tech_posts) New blog post about python cd0b57c (tag: v1.0, tag: my-first-commit, master) Initial commit
❯ git show 2b0b3f2 commit 2b0b3f24f3d7e8df809e46eb10c11ba66139acb8 (HEAD -> tech_posts) Author: Nina Zakharenko <nina@nnja.io> Date: Sun Sep 24 14:55:35 2017 -0700 New blog post about python diff --git a/posts/python.txt b/posts/python.txt
1 2 3 4 5 6 7 8 9
❯ git revert 2b0b3f2 [tech_posts a08a108] Revert "New blog post about python" 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 posts/python.txt
❯ git log --oneline a08a108 (HEAD -> tech_posts) Revert "New blog post about python" 2b0b3f2 New blog post about python cd0b57c (tag: v1.0, tag: my-first-commit, master) Initial commit
cat hello.template [greeting] [noun]! This is a test of the emergency git-casting system.
git log --name-status --follow --oneline hello.template # Let's find the commit where hello.txt was renamed to hello.template 4b2b90e (HEAD -> exercise6, origin/exercise6) Replacing greeting with tokens for i18n R073 hello.txt hello.template fec9e7b Changing Hello to Hola M hello.txt afa34a6 Changing World to Mundo M hello.txt e348ebc (tag: my-exercise3-tag, tag: exercise3-annotated-tag, origin/exercise3, exercise3) Testing the emergency git-casting system M hello.txt 43388fe (origin/master, origin/exercise7, origin/exercise4, origin/exercise2, origin/HEAD, master, exercise2) Initial commit A hello.txt
git checkout fec9e7b -- hello.txt # Now let's checkout hello.txt from one commit before then
cat hello.txt Hola World! This is a test of the emergency git-casting system.
git reset HEAD hello.txt
git rm hello.template rm'hello.template'
git status On branch exercise6 Your branch is up to date with 'origin/exercise6'.
Changes to be committed: (use "git reset HEAD <file>..." to unstage)
deleted: hello.template
Untracked files: (use "git add <file>..." to include in what will be committed)
git log --diff-filter=D --oneline -- hello.template 6aca1c7 (HEAD -> exercise6) Deleting hello.template # Ah, it was deleted at 6aca1c7. Let's use the caret (^) syntax to checkout hello.template from one commit before that
git checkout 6aca1c7^ -- hello.template
cat hello.template [greeting] [noun]! This is a test of the emergency git-casting system.
Use git clean to remove untracked files from your repo. Remember to use --dry-run first.
1 2 3 4 5
git clean --dry-run Would remove hello.txt
git clean -f Removing hello.txt
Stage a change and then use git reset to unstage it. Use git reset --hard to reset your branch back pointer, staging area, and working area to an earlier commit. Use “mixed mode” to reset your branch back to an earlier commit, then use ORIG_HEAD to reset your branch back to where you were.
git status On branch exercise6 Your branch is ahead of 'origin/exercise6' by 1 commit. (use "git push" to publish your local commits)
Changes to be committed: (use "git reset HEAD <file>..." to unstage)
new file: hello.template
git reset -- hello.template
git status On branch exercise6 Your branch is ahead of 'origin/exercise6' by 1 commit. (use "git push" to publish your local commits)
Untracked files: (use "git add <file>..." to include in what will be committed)
hello.template
nothing added to commit but untracked files present (use "git add" to track)
git log --oneline # Let's find the commit before we deleted hello.template 6aca1c7 (HEAD -> exercise6) Deleting hello.template 4b2b90e (origin/exercise6) Replacing greeting with tokens for i18n ff91b70 (origin/exercise5) Merging in mundo branch fec9e7b Changing Hello to Hola 4582f72 Merge branch 'exercise3' into exercise4 afa34a6 Changing World to Mundo 7ea8b01 Merge branch 'exercise3' into exercise4 e348ebc (tag: my-exercise3-tag, tag: exercise3-annotated-tag, origin/exercise3, exercise3) Testing the emergency git-casting system 43388fe (origin/master, origin/exercise7, origin/exercise4, origin/exercise2, origin/HEAD, master, exercise2) Initial commit
rm hello.template # Let's remove the untracked copy of hello.template
git reset 4b2b90e -- hello.template # Reset hello.template back to 4b2b90e # This will update the staging area with 'hello.template' from that commit. Unstaged changes after reset: D hello.template
git status On branch exercise6 Your branch is ahead of 'origin/exercise6' by 1 commit. (use "git push" to publish your local commits)
Changes to be committed: (use "git reset HEAD <file>..." to unstage)
new file: hello.template
Changes not staged for commit: (use "git add/rm <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory)
deleted: hello.template
cat hello.template cat: hello.template: No such file or directory # At this point, we could commit the file back into our repository if we wanted to.
git reset --hard HEAD HEAD is now at 6aca1c7 Deleting hello.template
git log -2 --oneline 6aca1c7 (HEAD -> exercise6) Deleting hello.template 4b2b90e (origin/exercise6) Replacing greeting with tokens for i18n
# Reset our repo back to 4b2b90e git reset 4b2b90e Unstaged changes after reset: D hello.template
git log -1 --oneline 4b2b90e (HEAD -> exercise6, origin/exercise6) Replacing greeting with tokens for i18n
Amend is a quick and easy shortcut that lets you make changes to the previous commit.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
❯ cat index.txt welcome.txt Welcome to my blog python.txt Why python is my favorite language
❯ git commit -m "Add a blog post about Python" [tech_posts 4080a79] Add a blog post about Python 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 A
❯ git add posts/python.txt
❯ git commit --amend [tech_posts de53317] Add a blog post about Python Date: Wed Sep 27 22:12:31 2017 -0700 2 files changed, 1 insertion(+) create mode 100644 A create mode 100644 posts/python.txt
COMMITS CAN’T BE EDITED
Remember, commits can’t be edited!
A commit is referenced by the SHA of all its data.
Even if the tree the commit points to is the same, and the author is the same, the date is still different!
A new commit is created.
Rebase
WHAT IS REBASE ANYWAY
Imagine our tech_posts and master branch have diverged.
We don’t want a messy merge commit in our history.
We can pull in all the latest changes from master, and apply our commits on top of them by changing the parent commit of our commits.
Rebase = give a commit a new parent (i.e. a new “base” commit)
REBASE: REWINDING HEAD
1 2 3 4
❯ git checkout tech_posts Switched to branch 'tech_posts' ❯ git rebase master First, rewinding head to replay your work on top of it... Applying: Add a blog post about Python
REBASE: APPLY NEW COMMITS
1 2 3
❯ git rebase master First, rewinding head to replay your work on top of it... Applying: Add a blog post about Python
POWER OF REBASING - REPLAY COMMITS
Commits can be:
edited
removed
combined
re-ordered
inserted
Before they’re “replayed” on top of the new HEAD.
INTERACTIVE REBASE (REBASE -I OR REBASE –INTERACTIVE)
Interactive rebase opens an editor with a list of “todos”
in the format of: <command> <commit> <commit msg>
git will pick the commits in the specified order, or stop to take an action when editing or a conflict occurs.
interactive rebase with a shortcut:
git rebase -i <commit_to_fix>^
(the ^ specifies the parent commit)
REBASE OPTIONS
pick
keep this commit
reword
keep the commit, just change the message
edit
keep the commit, but stop to edit more than the message
squash
combine this commit with the previous one. stop to edit the message
fixup
combine this commit with the previous one. keep the previous commit message
exec
run the command on this line after picking the previous commit
drop
remove the commit (tip: if you remove this line, the commit will be dropped too!)
EXAMPLE REBASE
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
❯ git log --oneline a37be8d (HEAD -> tech_posts) Oops, I forgot an index entry for my new post 6857c3d posts/django-framework.txt 2733233 Add a blog post about Python
❯ git rebase -i 6857c3d^ # editor will open reword 6857c3d posts/django-framework.txt squash a37be8d Oops, I forgot an index entry for my new post # editor opens twice. # 1. reword (change commit) # 2. squash (combine commits) ❯ git log --oneline 9413427 (HEAD -> tech_posts) Add a new blog post explaining the pros and cons of django 2733233 (master) Add a blog post about Python 25b3810 Add a README file to the project cd0b57c (tag: v1.0, tag: my-first-commit) Initial commit
TIP: USE REBASE TO SPLIT COMMITS
Editing a commit can also split it up into multiple commits!
Start an interactive rebase with rebase -i
mark the commit with an edit
git reset HEAD^
git add
git commit
repeat (4) & (5) until the working area is clean!
git rebase --continue
Fixup & Autosquash
TIP: “AMEND” ANY COMMIT WITH FIXUP & AUTOSQUASH
What if we want to amend an arbitrary commit?
git add new files
git commit --fixup <SHA>
this creates a new commit, the message starts with ‘fixup!’
git rebase -i --autosquash <SHA>^
git will generate the right todos for you! just save and quit.
FIXUP & AUTOSQUASH EXAMPLE
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
❯ git log --oneline 9413427 (HEAD -> tech_posts) Add a new blog post explaining the pros and cons of django 2733233 (master) Add a blog post about Python 25b3810 Add a README file to the project cd0b57c (tag: v1.0, tag: my-first-commit) Initial commit # I want to edit commit 27332 (Add blog post about Python) ❯ git add posts/python.txt ❯ git commit --fixup 2733233 [tech_posts 5e980a7] fixup! Add a blog post about Python 1 file changed, 1 insertion(+), 1 deletion(-)
❯ git log --oneline 5e980a7 (HEAD -> tech_posts) fixup! Add a blog post about Python # git added new commit 5e980 message is: fixup! Add a blog post about Python 9413427 Add a new blog post explaining the pros and cons of django 2733233 (master) Add a blog post about Python 25b3810 Add a README file to the project cd0b57c (tag: v1.0, tag: my-first-commit) Initial commit
1 2 3 4 5 6 7 8 9 10 11
❯ git rebase -i --autosquash 25b3810^ # use the -i and --autosquash flags && use ref of the parent commit pick 25b3810 Add a README file to the project pick 2733233 Add a blog post about Python fixup 5e980a7 fixup! Add a blog post about Python # git will automatically reorder commits, and mark the commit with fixup action pick 9413427 Add a new blog post explaining the pros and cons of django
❯ git log --oneline 68377f4 (HEAD -> tech_posts) Add a new blog post explaining the pros and cons of django a8656d9 Add a blog post about Python # shiny new fixed up commit! 25b3810 Add a README file to the project cd0b57c (tag: v1.0, tag: my-first-commit) Initial commit
REBASE –EXEC (EXECUTE A COMMAND)
$ git rebase -i —exec “run-tests” <commit>
2 options for exec:
add it as a command when doing interactive rebase
use it as a flag when rebasing
When used as a flag, the command specified by exec will run after every commit is applied.
This can be used to run tests.
The rebase will stop if the command fails, giving you a chance to fix what’s wrong.
Abort
PULL THE RIP CORD
At any time before rebase is done, if things are going wrong:
git rebase --abort
REBASE PRO TIP
Before you rebase / fixup / squash / reorder:
Make a copy of your current branch:
git branch my_branch_backup
git branch will make a new branch, without switching to it
If rebase “succeeds” but you messed up…
git reset my_branch_backup --hard
You’re back in business!
REBASE ADVANTAGES
Rebase is incredibly powerful!
You can slice and dice your git history.
It’s easy to fix previous mistakes in code.
You can keep your git history neat and clean.
COMMIT EARLY & OFTEN VS GOOD COMMITS
Git Best Practice:
“commit often, perfect later, publish once”
When working locally:
Commit whenever you make changes!
It’ll help you be a more productive developer.
Before pushing work to a shared repo:
Rebase to clean up the commit history
WARNING: NEVER REWRITE PUBLIC HISTORY
Rebase commits are copies
If other people are working on the same repository they would be working on different commits.
You could cause massive merge conflicts
Even worse, you can cause people to lose their work!
Warning: If you rewrite history and push to a public repo, monsters will eat you!
git commit -m "Adding a new feature" [exercise7-2 6aa0c97] Adding a new feature 1 file changed, 1 insertion(+) create mode 100644 feature.txt
git checkout master Switched to branch 'master' Your branch is up to date with 'origin/master'.
echo"Master has continued to change" >> hello.txt # The double arrow >> means 'append' to the file instead of overwrite.
git add hello.txt
git commit -m "Master has continued to change" [master a6ff221] Master has continued to change 1 file changed, 1 insertion(+)
git checkout exercise7-2 Switched to branch 'exercise7-2'
git rebase master First, rewinding head to replay your work on top of it... Applying: Adding a new feature
# Check that the changes to master are incorporated into our feature branch
git log --oneline b265f83 (HEAD -> exercise7-2) Adding a new feature a6ff221 (master) Master has continued to change 43388fe (origin/master, origin/exercise7, origin/exercise4, origin/exercise2, origin/HEAD, exercise2) Initial commit
Make another change to your current branch. Use an interactive rebase (git rebase -i) to rebase the two branches. Try squashing your two commits and rewording the message during the rebase.
git commit -m "Adding another new feature" [exercise7-2 075490a] Adding another new feature 1 file changed, 1 insertion(+) create mode 100644 another_feature.txt
git log -n 3 --oneline 075490a (HEAD -> exercise7-2) Adding another new feature b265f83 Adding a new feature a6ff221 (master) Master has continued to change
git rebase -i HEAD~2 Successfully rebased and updated refs/heads/exercise7-2.
git log --oneline 075490a (HEAD -> exercise7-2) Adding another new feature b265f83 Adding a new feature a6ff221 (master) Master has continued to change 43388fe (origin/master, origin/exercise7, origin/exercise4, origin/exercise2, origin/HEAD, exercise2) Initial commit
Forks & Remote Repos
Github vs. Git
GITHUB VS GIT - THE KEY IS COLLABORATION
Git:
Open source version control software
Github:
Repository hosting
Browse code
Issues
Pull Requests
Forks
Remotes
A remote is a git repository stored elsewhere - on the web, in github, etc.
origin is the default name git gives to the server you cloned from.
Cloning a remote repository from a URL will fetch the whole repository, and make a local copy in your .git folder.
In Github, go to https://github.com/nnja/advanced-git-exercises and create your own fork of this repo by clicking the Fork button in the top right corner. This should create a copy of the repo that belongs to you.
Look at your git remotes. Rename your origin remote (nnja’s copy) to upstream. Add your personal fork as the origin remote.