Skip to content

Instantly share code, notes, and snippets.

@esayler
Last active July 15, 2024 11:46
Show Gist options
  • Save esayler/a75c3e2c494e0fecbd5ddd559b278369 to your computer and use it in GitHub Desktop.
Save esayler/a75c3e2c494e0fecbd5ddd559b278369 to your computer and use it in GitHub Desktop.

Table of Contents

  1. Setting Up a Repo
  2. git init
  3. git clone
  4. git config
  5. Saving Changes
  6. git add
  7. git commit
  8. git commit
  9. Inspecting a repository
  10. git status
  11. git log
  12. Viewing old commits
  13. git checkout
  14. Undoing Changes
  15. git revert
  16. git reset
  17. `git clean
  18. Rewriting history
  19. git commit --ammend
  20. git rebase
  21. git rebase -i
  22. git reflog

Setting Up a Repo

git init

  • creates a new Git repo
  • convert an existing project or initialize a new empty repo
  • creates a .git subdirectory in project root
  • .git contains metadata for the repo

Usage

command meaning
git init transform current directory into Git repo
git init <directory> create empty Git repo in specified directory
git init --bare <directory> initialize empty Git repo, omit the working directory (for shared repos)

Note: repos initialized with --bare conventionally end in .git (e.g. my-project.git)

Bare Repositories

  • --bare flag creates a repo without a working directory (makes it impossible to edit files and commit changes in that repo)
  • central repos should always be created as bare repositories (pushing branches to a non-bare repo has the potential to overwrite changes)
  • think of --bare as marking a repository as a storage facility, not a development environment

Example

git clone used to create local copies of a project, so git init is usually used to create a central repo

$ ssh <user>@<host>              # 1. `ssh` into the server that will contain your central repo
$ cd path/above/repo             # 2. navigate to location to store the project
$ git init --bare my-project.git # 3. use the `--bare` flag to create a central storage repo

                                 # 4. other developers would then `git clone
                                 #    my-project.git` to create a local copy on their
                                 #    own machine

git clone

  • copies an existing Git repo
  • automatically creates a remote connection called origin pointing back to the original repo

Usage

command meaning
git clone <repo> clone the repo onto local machine
git clone <repo> <directory> clone <repo> into the <directory> on local machine

Discussion

  • git clone is most common way to obtain a dev copy of a central repo
  • you push or pull commit from one repo to another

Example (obtain local copy of central repo)

$ git clone ssh://[email protected]/path/to/my-project.git # 1. init new local git repo within `my-project`
$ cd my-project                                           # 2. `cd` into the project
                                                        # 3. start editing/working on project

git config

  • Lets you configure your Git install (or an individual repo)
  • define user info, preferences, behavior of repo

Usage and Common Configuration Options

command meaning
git config user.name <name> define author name for all commits in current repo
git config --global user.name <name> define author name to be used for all commits by the current user
git config --global user.email <name> define author email for all commits by current user
git config --global alias.<alias-name> <git-command> create an shortcut for a Git command
git config --system core.editor <editor> define text editor used by commands like git commit for all users on current machine
git config --global --edit open the global config file in a text editor

Discussion

all config options stored in three plaintext files:

location description
<repo>/.git/config repository-specific config
~/.gitconfig user-specific config (--global)
~$(prefix)/etc/gitconfig system-wide config
  • local settings override user settings, which override system-wide settings

config file example:

[user]
name = John Smith
email = [email protected]
[alias]
st = status
co = checkout
br = branch
up = rebase
ci = commit
[core]
editor = vim

Note: above aliases are SVN-like!

#Saving Changes

git add

  • adds a change in the working directory to the staging area
  • tells Git you want to include updates to a particular file in the next commit
  • changes are not recorded until you run git commit
  • you'll use git status to view the state of the working directory and staging area

Usage

command meaning
git add <file> stage all changes in <file> for next commit
git add <directory> stage all changes in <directory> for next commit
git add -p begin interactive staging session that lets you
choose portions of a file to add to next commit

| | - will present you with a chunk of changes + prompt you for a command | | | - use y to stage the chunk | | | - use n to ignore the chunk | | | - use s to split it into smaller chunks | | | - use e to manually edit the chunk | | | - use q to exit |

Discussion

  • fundamental Git workflow: git add + git commit
  • developing a project -> revolves around edit/stage/commit pattern
  1. edit your files
  2. when you're ready to save a copy of the current state, stage changes with git add
  3. once you're happy with staged snapshot, commit it to the project history with git commit
  • git add only needs to be called every time you alter a file

The Staging Area

  • Big idea: buffer between the working directory and project history
  • allows ability to group related changes into highly focused snapshots before actually committing to project history
  • allows you to edit unrelated files, then retroactively split edits up into seperate logical commits by adding related changes to the stage and committing each of them piece-by-piece
  • gives the ability to avoid committing all changes you've made since last commit
  • atomic commits important = easier to track down bugs and revert changes with minimal impact on the rest of project

Example

$ git add .        # 1a.
$ git commit       # 1b. create an initial commit of the current directory

$ git add hello.py # 2a. stage new file and its changes
$ git commit       # 2b. commit changes
  • new files can be added by passing their path to git add
  • above commands also can be used to record changes to existing files (Git doesn't differentiate between staging changes in new files vs. changes in files that already have been added to repo)

git commit

  • commits staged snapshot to the project history of the local repository
  • committed snapshots = "safe" versions of a project

Usage

command meaning
git commit commit the staged snapshot, will launch an editor to compose commit message
git commit -m "message" commit the staged snapshot with as the commit message
git commit -a commit a snapshot of all changes of tracked files (files added with git add )
  • NOTE: for git commit, enter the commit message and save and close to create the commit

Discussion

  • snapshots always commit to local repo
  • staging allows for accumulation of commits in local repo before interacting with the central repo
  • makes it easier to: split up a feature into atomic commits, keep related commits grouped together, clean up local history before publishing to the central repo
  • lets developers work in an isolated environment, deferring integration until their at a convenient break point

Snapshots, Not Differences

  • Git's version control model based on snapshots
  • Git records the entire contents of each file in every commit
  • record entire file: makes Git faster = particular version of a file doesn't need to be "assembled" from its diffs

Example

the following assumes you edited some content in hello.py and are ready to commit:

git add hello.py
git commit
  1. stage the file with git add
  2. commit the staged snapshot using git commit
  3. enter the commit message in the text editor set by git config, save and close the editor to make the commit
  • summarize the entire commit with 1st line of commit message in less than 50 chars
  • leave a blank line
  • then a detail explanation of what's been changed
  • Note: developers like to use present tense in commit messages
  • this makes them read like actions on the repo, makes many of the history-rewriting operations more intuitive

Inspecting a repository

git status

displays the current state of working directory and staging area

  • lets you see:
  • which changes are staged
  • which changes not staged
  • which files aren't being tracked by Git
  • doesn't show you committed project history (use git log)

Usage

  • git status
  • lists which files are staged, unstated, and untracked

Discussion

  • git status shos you results of using git add and git commit
  • status messages contain instructions on how to stage/upstage files

Ignoring Files

  • two categories of untracked files"
  1. files that have just been added
  2. compiled binaries
  • Git allows you to completely ignore files by placing paths in a .gitignore file
  • files that you'd like to ignore are included on a separate line
  • * can be used as a wild-card

Example

  • good practice to check the state of the repo before committing changes

git log

  • git log displays committed snapshots
  • lets you list the project history, filter it, and search for specific changes
  • log output can be customized (filtering commits, custom display formatting)

Usage + Common Configurations

command meaning
git log display entire commit history, default formatting (Space = advance, q = exit)
git log -n <limit> limit number of commits displayed by a <limit> integer
git log --oneline condense each commit to a single line
git log --stat include which files were altered + relative # of lines added/deleted
git log -p display patch (full diff) for each commit (most detailed view)
git log --author="<pattern>" search for commits by author, "<pattern>" arg can be string or regrex
git log --grep="<pattern>" search for commits by commit message that matches string or regex "<pattern>"
git log <since>..<until> show only commits between <since> and <until> (can use commit ID, branch name, HEAD or other revision reference
git log <file> only display commits including <file>
git log --graph --decorate --oneline * --graph draws a text graph of commits on the left side of commit messages
* --decorate adds names of branches or tags of commits that are shown
* --oneline shows the commit info on a single line making it easier to browse

Discussion

  • git log = basic tool used to explore a repo's history (i.e. starting point for finding commits)
  • use git log to find a specific version of a project or figure out what changes will be introduced by merging in a feature branch
  • HEAD refers to the current commit
  • ~ is useful for making relative references to the parent of a commit (e.g. 3157e~1 refers to the commit directly before 3157e and HEAD~3 is the great-grandparent of the current commit)
  • ID methods allow you to perform actions based on specific commits

Example

  • several options can be combined in a single command:
command meaning
git log --author="John Smith" -p hello.py display a full diff of all changes John Smith made to the file hello.py
git log --oneline master..some-feature - display a brief overview of all commits in some-feature + not in master
- .. syntax useful for comparing branches:

Viewing old commits

git checkout

load saved snapshots into development machine

allows you do perform 3 distinct operations:

  1. check out a file (see an old version of a particular file)
  2. check out a commit (makes entire working directory match a commit, see an old state of your project)
  3. check out a branch

Usage

command meaning
git checkout master return to master branch ("current" state of the project)
git checkout <commit> <file> * view previous version of a file
* (turns in the working directory into
an exact copy of the one from <commit> and adds it to the staging area)
git checkout <commit> update all files in working direcotry to
match specified commit (puts you in detached HEAD state)

Discussion

  • purpose of version control = store 'safe' copies of a project -> avoid worrying about irreparably breaking code

  • checking out an old commit is read-only operation ("current" state of project remains untouched in master)A

  • "detached HEAD" state

    • normally, HEAD points to master or other local branch
    • when you git checkout <previous commit>, HEAD no longer points to a branch, but to a commit
  • use git checkout git checkout <commit> <file> to revert back to an old version of an individual file ()

    • checking out out an old file using git checkout <commit> <file> does affect current state of your repo
    • you can re-commit the old version in a new snapshot as you would any other file

Example

Viewing an Old Revision

This example assumes you're developing a crazy experiment, but not sure if you want to keep it or not:

$ git log --oneline                     # 1. find the ID of the revision you want to see

b7119f2 Continue doing crazy things
872fa7e Try something crazy
a1e8fb5 Make some changes to hello.py
435b61d Create hello.py
9773e52 Initial import                  # 2. Project history


$ git checkout a1e8fb5                  # 3. use `git checkout` to view "make some import
                                        # changes to hello.py." commit:

                                        # - makes working dir match exact state of `a1e8fb5` commit
                                        # - you make changes without losing current state of project
                                        # - nothing you do here will be saved in your repo

$ git checkout master                   # 4. Get back to "current" state of project to continue developing

Note: above assumes that you're developing on the default master branch

  • once on the master branch you can use git revert or git reset to undo undesired changes

Checking Out a File

If only interested in a single file, use git checkout to fetch an old version of it

e.g. only want to see hello.py from the old commit:

git checkout a1e8fb5 hello.py

the above will affect current state of project: the old file revision will show up as a "Change to be committed", giving you the opportunity to revert back to the previous version of the file

If you don't want to keep the old version, check out the most recent version with:

git checkout HEAD hello.py

Undoing Changes

git checkout

see above section

git revert

  • designed to safely remove public commits

  • git revert undoes a committed snapshot by figuring out how to undo changes and creates a new commit with the resulting content (instead of removing the commit)

  • prevents git from losing history

Usage

command meaning
git revert <commit> generate a new commit that undoes all the changes
introduced in <commit> and apply it to the current branch

Discussion

Reverting should be used when you want to remove an entire commit from your project history

Useful if you're tracking down a bug and find it was introduced by a single commit

Reverting vs. Resetting

git revert undoes changes belonging to a single commit by adding a new commit that undos those changes

(git revert does not "revert" back to the previous state of a project by removing all subsequent commits)

to return to a previous state of a project by removing all commits since a certain commit, use reset

git revert has 2 advantages over git reset:

  1. git revert doesn't change project history (making it a "safe" operation for commits already publish to shared repo) by adding a new commit

  2. git revert is able to target individual commits at an arbitrary point in history, git reset can only work backwards from current commit (with git reset, to undo an old commit with git reset, you would have to remove all commits that occured after the target commit, remove it, then recommit all the subsequent commits)

Example

# Edit some tracked files

# Commit a snapshot
git commit -m "Make some changes that will be undone"


# revert the commit we just created
git revert HEAD

# (NOTE: `HEAD` refers to the current commit)

git reset

git reset designed to undo local changes

git reset is a permanent undo:

its the "dangerous" method of undoing changes (vs. git revert) - commonly used to undo uncommitted but staged changes

undoing with git reset = commits no longer referred by any ref or in reflog, no way to retrieve original copy

can be used to remove committed snapshots, usually used to undo changes in the staging area/working directory

should only be used to undo local changes - never reset snapshots shared with other developers

Usage

command meaning
git reset <file> unstage file without overwriting changes in working dir
git reset unstage all files (to match most recent commit) without overwriting changes (allows you to re-build the staged snapshot from scratch)
git reset --hard reset the staging area and working dir to most recent commit (overwrites all changes in working dir) (deletes all uncommitted changes)
git reset <commit> move current branch tip backward to <commit>, reset staging area to match, leave working dir alone (all changes since <commit> remain in working dir, letting you recommit project history using cleaner, more atomic snapshots)
git reset --hard <commit> move current branch tip backward to <commit>, reset both staging area and working dir (deletes all uncommitted changes + commits after <commit>)

Discussion

without --hard, use git reset to clean up a repo by unstaging changes/uncommited a series of snapshots and re-building from scratch

Use --hard if an experiment has gone horribly wrong and you need a clean slate

Don't Reset Public History

Never use git reset <commit> if any snapshots after <commit> have been pushed to a public repo

after publishing a commit, assume other developers are reliant upon it

the merge commit required to synchronize repos after resetting a public commit is confusing

use git revert to fix a public commit

Examples

Unstaging a File

git reset frequently encountered while preparing the staged snapshot

This example assumes you have two files called hello.py and main.py already added to the repo:

                                # 1. Edit both hello.py and main.py

git add .                       # 2. Stage everything in current dir

                                # 3. Realize that the changes in hello.py and main.py
                                # should be committed in different snapshots

git reset main.py               # 4. Unstage main.py

git commit -m "change hello.py" # 5. Commit only hello.py

git add main.py

git commit -m "Edit main.py"    # Commit main.py in a separate snapshot

git reset allows you to keep commits highly-focused by letting you unstage changes that aren't related to the next commit

Removing Local Commits

This example demonstrates deciding to throw away a new experiment after committing a few snapshots:

                                             # 1. Create a new file called `foo.py` and add some code to it

git add foo.py
git commit -m "start crazy feature"          # 2. Commit changes

                                             # 3. Edit `foo.py` and change other tracked files

git commit -a -m "Continue my crazy feature" # 4. Commit another snapshot

git reset --hard HEAD~2                      # 5. Decide to scrap feature, remove the associated commits

git reset --hard HEAD~2 moves current branch back two commits, effectively removing 2 snapshots just created

NOTE: This kind of reset should only be used on unpublished commits

git clean

Convenience command that removes untracked files from working dir

like rm, git clean is not undoable

often executed in conjunction with git reset --hard (reseting only affects tracked files, so git clean is used to clean up untracked ones

combination of git reset --hard and git clean = return working dir to exact state of a particular commit

Usage

command Description
git clean -n perform a 'dry run' to show files to be removed
git clean -f remove untracked files from current dir (-f forces the action)
git clean -f <path> remove untracked files within only
git clean -df remove untracked files and untracked directories from current dir
git clean -xf remove untracked files from current dir + any files Git usually ignores

NOTE: if configuration clean.requireForce = false, -f required when running git clean (default is true)

Discussion

if you made some embarrassing developments in local repo and want to burn the evidence, use git reset --hard and git clean -f

running both git reset --hardandgit clean -f` makes working dir match most recent commit

git clean can also be used to clean up working dir and a build (e.g. remove binaries generated by a compiler) (-x flag is useful in this situation)

git reset and git clean are both among the few git commands able to permanently delete commits

Example

The following 'obliterates' all cahnges in the working directory, including new files that have been added.

                 # 1. Edit some existing files
                 # 2. Add some new files
                 # 3. Realize you have no idea what you're doing

git reset --hard # 4. Undo changes in tracked files

git clean -df    # 5. Remove untracked files
  • after running this reset/clean sequence, the working directory and the staging area will look exacty like the most recent commit, and git status will report a clean working directory.

  • unlike the 2nd example in git reset, the new files were not added to the repository. As a result, they could not be affect by git resedt --hard and git clean was required to delete them.

Rewriting History

intro

git commit --amend

git rebase

git rebase -i

git reflog

Collaborating

Syncing

git remote

git fetch

git pull

git push

Making a Pull Request

Intro

Anatomy of a Pull Request

Feature Branch Workflow With Pull Requests

Gitflow Workflow With Pull Requests

Formking Workflow With Pull Requests

Intro

Mary forks the official repo

Mary clones her Bitbucket repo

Mary develops a new feature

Mary pushes the feature to her Bitbucket repo

Mary creates the pull request

John reviews the pull request

###Mary adds a follow-up commit

John accepts the pull request

Using Branches

Intro

git branch

git checkout

git merge

Comparing Workflows

Intro

Centralized Workflow

Managing Conflicts

Feature Branch Workflow

Pull Requests

Gitflow Workflow

Forking Workflow

Advanced Git Tutorials

Merging vs. Rebasing

Resetting, Checkout Out, and Reverting

Advanced Git Log

Git Hooks

Refs and Reflog

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment