DEV Community

Pere Sola
Pere Sola

Posted on • Updated on

Advance Git course notes - Frontend Masters by Nina Zakharenko

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:

image

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.

image

image

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.

image

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.

image
image
image
image

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.

image

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.

image
image

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.

image

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:

image
image

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.

image

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.

image

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.

image

GIT SHOW

Look at a commit.

git show <commit> show commits and contents
git show <commit> --statshow files changes in commit
git show <commit>:<file> look at a file from another commit

i.e. git show e47e1cd

image

i.e. git show e47e1cd --stat --oneline

image

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

image

What happens when we git checkout a branch?
  1. Change HEAD to point to the new branch
  2. Copy commit snapshot to the staging area (also called index)
  3. Update the working area with the branch content

image

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.

image

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.

image

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.

image

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:

image

After:
image

GIT RESET --MIXED (the default): move HEAD, copy files to stage

Before:
image

After:
image

GIT RESET --HARD: move HEAD, copy files to stage & working! (destructive operation because you lose files untracked in the working area)

Before:
image

After:
image

git reset --hard cleans staging and working trees.

CHEATSHEET GIT RESET :

image

GIT RESET --

Doesn't move the HEAD pointer but resets file in staging area.

Before:
image

After:
image

GIT RESET --

Same thing as git reset -- <file_path> - copies files from commit whatever into staging area.

Before:
image

After:
image

CHEATSHEET GIT RESET --

image

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.

image

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.

image
image

REBASE, AMEND - REWRITE HISTORY!

AMEND A COMMIT

git commit --amend Quick and easy shortcut that lets you make changes to the previous commit.

image
image

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.

image
image
image
image
image

MERGE VS REBASE

Rebase is cleaner because it doesn't create a new commit.

image

INTERACTIVE REBASE

Commits can be edited, removed, combined, re-ordered, inserted. Before they are "replayed" on top of the new HEAD.

git rebase -ior 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).

image
image

image

REBASE TO SPLIT COMMITS

Editing a commit can also split it up into multiple commits!

image

FIXUP AND AUTOSQUASH

image
image

image

GIT REBASE --ABORT

At any time before rebase is done, if things are going wrong: git rebase --abort.

image

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.

image

CHECK EXERCISE 7 SOLUTIONS FOR EXAMPLES ABOUT REBASE -I HERE

GITHUB AND REMOTE REPOS

image

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).

image

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

image

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.

image

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.

image

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.

image

image
image

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.

Screenshot 2021-10-08 at 09.24.16
Screenshot 2021-10-08 at 09.25.17
Screenshot 2021-10-08 at 09.25.54

GITHUB

NAVIGATE LIKE A PRO

Screenshot 2021-10-08 at 09.27.01

Up to date list of shortcuts here.

CONTINUOUS INTEGRATION (CI)

Screenshot 2021-10-08 at 09.32.27

Travis CI integrates with GH:

Screenshot 2021-10-08 at 09.33.09

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)

Collapse
 
devanpellow profile image
devan☕

Wow this is great work! Saving this!