These are my notes from Nina Zakharenko's Git course on FrontendMasters. Repo can be found here. Well good luck to me! :)
On windows, the equivalent of tree .git
in Mac is tree.com //a //f
when inside the folder we want to map. For instance, .git
. Source.
Plumbing commands
echo 'Hello, World!' | git hash-object --stdin
returns a SHA1: 8ab686eafeb1f44702738c8b0f24f2567c36da6d. --stdin
is because otherwise it expects a file.
^ in Mac we get the same with echo 'blob 14\Hello, World!' | openssl sha1
git init
creates .git
folder, which contains data about our repo.
git hash-object
echo 'Hello, World!' | git hash-object -w --stdin
we run it now with the -w
flag (Write)
The hash has been saved in directory objects
, folder 8a
(first 2 chars of hash), which stores the rest of the hash: b686ea
. But the blob (which stores the hash) is missing filenames and directory. Git stores this information in a tree
. A tree contains pointers (using SHA1) to:
- blobs
- to other trees
And metadata:
- type of pointer (blob or tree)
- filename or directory name
- mode (executable file, symbolic link, etc)
git cat-file
git cat-file -t 8ab68
(first few digits of hash) -t
prints the type of the hash
git cat-file -p 8ab68
(first few digits of hash) -p
prints the content of the hash
We don't have to type the whole hash because, unless your repo is massive (i.e. linux kernel), git can figure out the hash with the first few characters. Otherwise, git will tell you..
git config
git config --global core.editor <YOUR_EDITOR>
emacs: emacs
vi: vi
or vim
atom: atom --wait
sublime: subl -n -w
vscode: code --wait
git ls-files
git ls-files -s
shows what's in the staging area:
Commits (slide ~30)
A commit is a code snapshot at a point in time. They point to a tree and contain metadata:
- author and committer
- date
- message
- parent commit (one or more)
The SHA1 of the commit is the hash of all this information.
We can't change commits because any data change will have a new hash. So even if same content, date will change so hash will change.
References - pointers to commits
2 places where references are stored:
-
.git/HEAD
file -
.git/refs/heads
is where all branches live
git log --oneline
tells me which commit HEAD is pointing to.
git log --oneline --graph
add a graphical component to it
cat .git/refs/heads/main
will print hash of ^ commit where HEAD is pointing to (which commit branch points to)
cat .git/HEAD
shows that, at this point in time, HEAD (our current branch pointer) is also pointing to main. And main is pointing to the initial commit.
git show-ref --heads
Tags
Simple pointer to a commit. When a tag is created with no arguments, it captures the value in HEAD.
git tag -a
annotated tags. Points to a commit but stores additional information. i.e. git tag -a v1.0 -m "Version 1.0 of my blog".
git tagwill print commit tag is pointing to.
git show v1.0` will print all the additional information.
git tag
lists all the tags in a repo
git show-ref --tags
lists all tags and commits they are pointing to
git tag --points-at <commit>
list tags pointing to a commit
git show <tag-name>
list info about tag
The commit that a tag points to does not change, it is a snapshot.
Branches
Pointer to a particular commit. The pointer of the current branch changes as new commits are made.
The current branch pointer moves with every commit to the repository.
HEAD
(a special type of reference) a pointer to the current commit (the very last commit you made). That is how git knows what branch you are currently on and what the next parent will be. Points to the name of the current branch but it can point at a commit too (detached HEAD). HEAD moves when you commit in current active branch or when you checkout a new branch.
HEAD-LESS / DETACHED HEAD STATE
When you checkout to a specific commit or tag instead of a branch. There is no reference pointing to the commits you made in a detached state. If you want to save your work: git branch <new-branch-name> <last-commit>
. If you don't point a new branch at those commits, they will no longer be referenced in git (dangling commits) and will be garbage collected.
3 AREAS WHERE CODE LIVES
WORKING AREA (wORKING TREE)
Also called untracked files
. Files in working area not in staging are not handles by git.
STAGING AREA (aka CASH, INDEX)
What files are going to be part of your next commit. This is how git know what will change between your current commit and your next commit.
A clean staging area isn't empty. It is an exact copy of the latest commit. When files are added or removed from the staging area git knows because the SHA1 of your files is different from the ones in your repository.
git add <file>
git add -p
allows to stage in chunks, interactively. Useful if you have done too much work for one commit and you want to break it out. -p for partial?
git rm <file>
remove file
git mv <file>
rename file
git reset hello.txt
removes the files from the staging area, without changing the file in the working area
Unstaging
files doesn't remove the files, it replaces them with a copy currently in the repository.
REPOSITORY
The files git knows about. Contains all of your commits. In your .git directory.
GIT STASH
save un-commited work and it is safe from destructive operations.
git stash
git stash list
git stash show stash@{0}
show contents of this stash
git stash apply
apply the last stash
git stash apply stash@{0}
this is the same as git stash apply
because it applies stash 0, which is the last one.
git stash --include-untracked
by default git only stages files that are already tracked - in the repo or in the staging area. What if you have created a file that you are not ready t move to the staging area? Once you are back, the untracked files are still untracked
git stash --all
will stash all files, include the ignored
git stash save "WIP: making progress on foo"
name the stash for easy reference
git stash branch <optional branch name>
start new branch from a stash
git checkout <stash name> -- <filename>
grabs a single file from a stash. if you have changes in your working area when the stash is applied the changes will be overwritten, so careful!
git stash pop
removes the last stash and apply the changes
git stash drop
removes the last stash
git stash drop stash@{n}
removes the nth stash
git stash clear
removes all stashes
MERGE AND REBASE
MERGE COMMITS
They are just commits. Marker of when a new feature is merged into master
.
FAST FORWARD
You checkout from master
, you work on your feature, and there have not been any changes to master
. When merging into master
git shows the message fast-forward
. Feature commits are added on top of master
and the pointer of master
is moved to the tip:
The danger is that we lose track of the feature branch, because there is a linear commit history. To avoid this, you can specify --no-ff
:
git merge --no-ff
it will foce a merge commit even when one it is not necessary.
MERGE CONFLICTS
Attempt to merge, but files have diverged. Git stops until conflicts are resolved. Git will try, but when it can't it will stop. Git creates a new file with the conflicts.
GIT RERERE - REuse REcorded REsolution
Git saves how you resolved a conflict and, in the next conflict, reuse the same resolution. Useful for:
- Long lived feature branch (like a refactor)
- Rebasing
Turn it on: git config rerere.enabled true
turns rerere in this project. If you want it for all your projects, use --global
flag when calling git config
.
git rerere diff
will show what is going on with the conflict.
HISTORY AND DIFFS
Good commits are important and help preserve teh history of a code base. Anatomy of a good commit message:
-
git commit -m
is fine if the commit message is brief and is one liner. If you need loger,git commit
will open up the editor. - Future tens: fix vs fixed.
- Short subject, followed by blank line. Description of current behaviour and short summary of why the fix is needed.
GIT LOG
git log
is the basic command. But can be cooler:
git log --since="yesterday"
git log --since="2 weeks ago"
git log --name-status --follow -- <file>
to follow logs for a specific file, even if it changes names!
image
^ A = Added, M = Modified, R = Renamed?
git log -grep <regexp>
or git log -grep="whatever"
search for commit messages that match a regular expression or whatever. i.e. git log --grep=mail --author=nina --since=2.weeks
DIFF-FILTER
Allows to include or exclude files that have been (A)dded, (D)eleted, (M)odified & more.
git log --diff-filter=R --stat
R for Renamed
git log --diff-filter=M --oneline
finds commits that have modified files
git log --diff-filter=R --find-renames
REFERENCING COMMITS
^
or ^n
- no args is like ^1
: the first parent commit. n
is the nth parent commit.
~
or ~n
- how many commits follow back from the first parent. n
number of commits back, following only 1st parent.
^
and ~
can be combined.
GIT SHOW
Look at a commit.
git show <commit>
show commits and contents
git show <commit> --stat
show files changes in commit
git show <commit>:<file>
look at a file from another commit
i.e. git show e47e1cd
i.e. git show e47e1cd --stat --oneline
GIT DIFF
Shows changes between commits, between staging area and the repository, what's in the working area.
git diff
unstaged changes (what could be in the next commit)
git diff --staged
staged changes (what will be in the next commit).
^ You can pass file arguments to see a specific file.
git diff A B
will show changes in B, that are not in A. Same as git diff A..B
.
DIFF BRANCHES
git branch --merged master
which branches are merged with master, and be cleaned up
git branch --no-merged master
which branches aren't merged with master yet
FIXINGS MISTAKES (checkout, reset, revert, clean)
GIT CHECKOUT
Allows to restore tree files or switch branches
What happens when we git checkout
a branch?
- Change HEAD to point to the new branch
- Copy commit snapshot to the staging area (also called index)
- Update the working area with the branch content
What happens when we git checkout -- <file_path>
?
--
indicates to git the end of the command and the beginning of the arguments. In case branch and file name are the same, --
indicates to git that we want the file.
Replace the working area copy with the version from the current staging area.
What happens when you git checkout <commit> -- <file_path>
?
Update staging area to match the commit and update the working area to match the staging area.
Restore a deleted file
git checkout <deleting_commit>^ -- <file_path>
^
indicates parent so we are restoring the commit before file got deleted.
GIT CLEAN
How to clean working area? git clean
deletes untracked files. Use the --dry-run
flag to see what would be deleted. The -f
flag to do the deletion. -d
will clean both files and directories.
GIT RESET
Reset performs different actions depending on the arguments:
- With a path
- Without a path
By default, git performs git reset --mixed
.
git checkout
will move the HEAD but the branch stays where it was. git reset
will move the HEAD and the branch reference, meaning the branch is now modified.
For commits: moves the HEAD pointer, optionally modifies files.
For file paths: does not move HEAD pointer, modifies files.
3 options for git reset
:
GIT RESET --SOFT: moves HEAD
git reset --soft HEAD~
now HEAD points to the previous commit (because ^
indicates parent of the current commit).
Before:
GIT RESET --MIXED (the default): move HEAD, copy files to stage
GIT RESET --HARD: move HEAD, copy files to stage & working! (destructive operation because you lose files untracked in the working area)
After:
image
git reset --hard
cleans staging and working trees.
CHEATSHEET GIT RESET :
GIT RESET --
Doesn't move the HEAD pointer but resets file in staging area.
GIT RESET --
Same thing as git reset -- <file_path>
- copies files from commit whatever into staging area.
CHEATSHEET GIT RESET --
UNDO A GIT RESET WITH orig_head
In case of an accidental git reset
. Git keep the previous value of HEAD in variable called ORIG_HEAD
. To go back to the way things were: git reset ORIG_HEAD
.
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 git revert
if undoing a commit that has already been shared. revert does not change history.
REBASE, AMEND - REWRITE HISTORY!
AMEND A COMMIT
git commit --amend
Quick and easy shortcut that lets you make changes to the previous commit.
REBASE = give a commit a new parent (i.e. a new "base" commit)
Imagine feature branch and master branch have diverged. We don't want a messe 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.
MERGE VS REBASE
Rebase is cleaner because it doesn't create a new commit.
INTERACTIVE REBASE
Commits can be edited, removed, combined, re-ordered, inserted. Before they are "replayed" on top of the new HEAD.
git rebase -i
or git rebase --interactive
and opens up an editor with the "todos".
Interactive rebase with a shortcut: git rebase -i <commit_to_fix>^
(the ^
specifies the parent commit).
REBASE TO SPLIT COMMITS
Editing a commit can also split it up into multiple commits!
FIXUP AND AUTOSQUASH
GIT REBASE --ABORT
At any time before rebase is done, if things are going wrong: git rebase --abort
.
Git best practice: commit often, perfect later, publish once
.
When working locally:
- Commit whenever you make changes
- It will help you be a more productive developer.
- Before push work to a shared repo, rebase to clean up the commit history.
CHECK EXERCISE 7 SOLUTIONS FOR EXAMPLES ABOUT REBASE -I HERE
GITHUB AND REMOTE REPOS
origin
name that GH gives to the server you cloned from. Cloning a repo from a URL will fetch the whole repo and make a local copy in your .git folder.e remotes
git remote -v
see remote you have set up
What if you clone someone's repository? You can pull the changes, but you cannot push.
FORK
GH concept. Copy of repo stored in your GH account. You can clone your fork to your local computer. And because that repo is in your account, there are no no restrictions. If you want to merge changes to the original project from a fork, you do that via a PR (Pull Request).
If you want your fork to stay up to date (while you work on your work, other changes are getting merged into the source repository) you need to set an upstream.
Upstream is the base repot you created the fork from. This isn't set up by default, you need to set it up manually. By adding an upstream remote, you can pull down changes that have been added to the original repo after you forked it.
git remote add upstream https://github.com/ORIG_OWNER/REPO.git
GH WORKFLOW
TRACK A BRANCH
Track a branch to tie it to an upstream branch. Use git pull
/ git push
with no arguments.
To checkout a remote branch, with tracking: git checkout -t origin/feature
.
Tell Git which branch to track the first time you push: git push -u origin feature
(it sets tracking and push that branch to remote).
git branch
can be improved with git branch -vv
to show which upstream (or remote branch) you are tracking in your remote branch and how many commits behind you are.
GIT FETCH
Keeps your local repository up to date with a remote. It pulls down all the changes that happened on the server. But it does not change your local repository.
GIT PULL
git pull
will pull down the changes from the remote repo to your local repo, and merging them with a local branch.
Under the hood: git pull
= git fetch && git merge
If changes happened upstream, wit will create a merge commit. Otherwise, it will fast-forward.
GIT PUSH
git push
sends your changes to the remote repo. Git only allows to push if your changes don't cause a conflict. To see commits which haven't been pushed upstream yet, use: git cherry -v
.
GIT PULL --REBASE
git pull --rebase
will fetch, update local branch to copy the upstream branch, then replay any commits you made via rebase. When you open a PR, there will be no unsightly merge commits.
NOTE ON TAGS
Git doesn't automatically push local tags to a remo repo, you need to do it manually. To push tags: git push <tagname>
or git push --tags
.
DANGER ZONE
These are all destructive actions:
git checkout -- <file>>
if the file is present in staging area it will be overwritten
git reset --hard
will overwrite changes that are stahed and in the working area
Unless changes are staged, there is no way to recover them.
Use git stash --include-untracked
to include working area changes in your stash.
REMOTE DESTRUCTIVE OPERATIONS - REWRITING HISTORY
- Rebase
- Amend
- Reset
If your code is hosted or shared, never run git push -f
HOW TO RECOVER LOST WORK
Use ORIG_HEAD
: the commit HEAD was pointing to before a: reset
, merge
. Check for repository copies: github, coworker.
GITHUB
NAVIGATE LIKE A PRO
Up to date list of shortcuts here.
CONTINUOUS INTEGRATION (CI)
Travis CI integrates with GH:
MISC
git branch a-new-branch
creates a branch but doesn't switch to it. Contrary to git checkout -b a-new-branch
, which switches to it.
git checkout -
switched to the previous branch we were in
Top comments (1)
Wow this is great work! Saving this!