Skip to main content

Basic

Difference between HEAD, working tree and index

git-transport.png

Source: My Git Workflow

Notes:

  • the workspace is the directory tree of (source) files that you see and edit.
  • The index is a single, large, binary file in <baseOfRepo>/.git/index, which lists all files in the current branch, their sha1 checksums, time stamps and the file name -- it is not another directory with a copy of files in it.
  • The local repository is a hidden directory (.git) including an objects directory containing all versions of every file in the repo (local branches and copies of remote branches) as a compressed "blob" file.

Don't think of the four 'disks' represented in the image above as separate copies of the repo files.

References

What is references?

If you were interested in seeing the history of your repository reachable from commit, say, 1a410e, you could run something like git log 1a410e to display that history, but you would still have to remember that 1a410e is the commit you want to use as the starting point for that history. Instead, it would be easier if you had a file in which you could store that SHA-1 value under a simple name so you could use that simple name rather than the raw SHA-1 value.

In Git, these simple names are called “references” or “refs”; you can find the files that contain those SHA-1 values in the .git/refs directory.

## Fake SHA-1 btw...
git show-ref
da39a3ee5e6b4b0d3255bfef95601890afd80709 refs/heads/master
da39a3ee5e6b4b0d3255bfef95601890afd12342 refs/heads/sai
da39a3ee5e6b4b0d3255bfef95601890afd12333 refs/remotes/origin/HEAD
da39a3ee5e6b4b0d3255bfef95601890afd12319 refs/remotes/origin/master
da39a3ee5e6b4b0d3255bfef95601890afd42343 refs/remotes/origin/mateusz
da39a3ee5e6b4b0d3255bfef95601890afd12312 refs/remotes/origin/sai

What is HEAD in Git?

HEAD answers the question: Where am I right now in the repository? It is a pointer to the currently checked out branch or commit, which in turn contains an immutable snapshot of your entire code base at a given time.

Whichever commit HEAD is referencing, directly (using the hash) or by reference (using a branch), it'll always be the commit on which any local changes are based.

Why understand the concept of HEAD is important?

Basically, with a good understanding of the HEAD pointer you're able to swiftly navigate the history of your repository and perform operations you see fit. Never again will you be lost in time, or misinterpret the information detached HEAD for an error!

Attached & detached state

First of all, it's important to know that the HEAD pointer can be in either of two states: attached or detached. Default state is attached, where any manipulation to the history is automatically recorded to the branch HEAD is currently referencing. In detached state, experimental changes can be made without impacting any existing branch, as HEAD is referencing the underlying commit directly and is not "attached" to a particular branch.

Take a look at below illustration, showcasing the two states in a side-by-side comparison. Note that our history only contains two commits (C0& C1), and one branch (master) with its remote counterpart (o/master).

HEAD.001

Source: What is HEAD in Git?

On the left hand side, HEAD is in default attached state where any changes are automatically recorded to a branch; in this particular case the local master branch. On the right, HEAD is in the slightly more uncommon detached state, referencing the commit using its unique hash 14ko3 directly.

HEAD.004

Source: What is HEAD in Git?

As you can see above, in both cases the new commit C2 got created through a commit operation. The main difference being that in attached state the change is automatically recorded in the master branch, whereas in detached state the change did not impact any existing branch and master remains referencing C1.

What is HEAD referencing?

As already mentioned, most of the time your HEAD reference will be pointing to a branch, and hence be in attached state. Whenever a new commit is created on the branch, HEAD will automatically follow. But, in some cases and during some procedures you‘ll find yourself with HEAD in detached state, where it directly references a commit rather than a branch.

For example, every time you explicitly checkout a commit using its hash, or if you checkout a tag, HEAD will go into detached state. There are also other operations such as interactive rebase that might leave you with HEAD in detached state.

@  the new HEAD alias

Since version 1.8.4 of Git, the @ symbol can be used interchangeably with HEAD, for convenience. For example, instead of typing $ git show HEAD --oneline, you can instead write $ git show @ --oneline.

Ancestry reference - Relative Commit References

You may know that you can reference commits by their SHA, by tags, branches, and the special HEAD pointer. Sometimes that's not enough, though. There will be times when you'll want to reference a commit relative to another commit. For example, there will be times where you'll want to tell Git about the commit that's one before the current commit...or two before the current commit. There are special characters called "Ancestry References" that we can use to tell Git about these relative references.

Those characters are:
  • ^ indicates the other parents (^2, for example, for a merge).
  • ~ indicates the first parent commit

Here's how we can refer to previous commits:

  • the parent commit – the following indicate the parent commit of the current commit
    • HEAD^
    • HEAD~
    • HEAD~1
  • the grandparent commit – the following indicate the grandparent commit of the current commit
    • HEAD^^
    • HEAD~2
  • the great-grandparent commit – the following indicate the great-grandparent commit of the current commit
    • HEAD^^^
    • HEAD~3

The main difference between the ^ and the ~ is when a commit is created from a merge. A merge commit has two parents. With a merge commit, the ^ reference is used to indicate the first parent of the commit while ^2 indicates the second parent. The first parent is the branch you were on when you ran git merge while the second parent is the branch that was merged in.

How to recover from 'detached HEAD' state?

A lot of git beginners believe the message You are in 'detached HEAD' state to be an error, when in reality, as we've just seen, it just describes the state your HEAD pointer is in. “Recovering” from a detached HEAD state is simple, just switch back to an existing branch or create a new one from where you’re currently at.

Commands

add, commit, rm, mv, checkout

You need to create a repository manually before pushing to a remote repository. There are some tools for you if you fancy to use your local CLI to create repositories. such as hub

# Initialize Git:
git init

# Get everything ready to commit:
git add .

# Get custom file ready to commit:
git add index.html

# Add and commit in one step:
git commit -am "Description..."

# Update all changes:
git add -u

# Tell Git not to track a file/files (The inverse of the git add command):
git rm index.html

# Remove file but do not track anymore:
git rm --cached index.html

# Move or rename files:
git mv index.html dir/index_new.html

# Undo modifications (restore files from latest commited version):
# The -- is a way to tell Git to treat what follows checkout as a file and not as a branch.
git checkout -- <file-name>

# Restore file from a custom commit (in current branch):
git checkout 6eb715d -- index.html

log

# **[helpful]** Show  the actual changes made to files and things that have been changed in the commit:
git log -p --stat

# Show stats and summary of commits:
git log --stat --summary

# **[helpful]** Show history of commits as graph-summary:
git log --oneline --graph --all --decorate

# Show oneline-summary of the last three commits:
git log --oneline -3

# Show only custom commits:
git log --author="Sven"
git log --grep="Message"
git log --until=2013-01-01
git log --since=2013-01-01

# Show only custom data of commit:
git log --format=short
git log --format=full
git log --format=fuller
git log --format=email
git log --format=raw

# Show every commit since special commit for custom file only:
git log 6eb715d.. index.html

# Show changes of every commit since special commit for custom file only:
git log -p 6eb715d.. index.html

# Show commits each contributor has added to the repository:
git shortlog

compare

# Compare modified files:
git diff

# Compare modified files and highlight changes only:
git diff --color-words index.html

# Compare modified files within the staging area:
git diff --staged

# Compare branches:
git diff master..branchname

# Compare branches like above:
git diff --color-words master..branchname^

# Compare commits:
git diff 6eb715d
git diff 6eb715d..HEAD
git diff 6eb715d..537a09f

# Compare commits of file:
git diff 6eb715d index.html
git diff 6eb715d..537a09f index.html

# Compare without caring about spaces:
git diff -b 6eb715d..HEAD or:
git diff --ignore-space-change 6eb715d..HEAD

# Compare without caring about all spaces:
git diff -w 6eb715d..HEAD
git diff --ignore-all-space 6eb715d..HEAD

# Useful comparings:
git diff --stat --summary 6eb715d..HEAD

releases & Version Tags

Tagging is generally used to capture a point in history that is used for a marked version release (i.e. v1.0.1). A tag is like a branch that doesn’t change. Unlike branches, tags, after being created, have no further history of commits.

# Show all released versions:
git tag

# Show all released versions with comments:
git tag -l -n1

# Create release version:
git tag v1.0.0

# **[helpful]** Create release version with comment:
git tag -a v1.0.0 -m 'Message'

# Checkout a specific release version:
git checkout v1.0.0

branch

Git branches are effectively a pointer to a snapshot of your changes. When you want to add a new feature or fix a bug—no matter how big or how small—you spawn a new branch to encapsulate your changes. This makes it harder for unstable code to get merged into the main code base, and it gives you the chance to clean up your future's history before merging it into the main branch.

# Create branch on specific commit:
git branch branchname a_commit_SHA

# Create and change to new branch:
git checkout -b branchname

# Rename branch:
git branch -m branchname new_branchname`

# Show all completely merged branches with current branch:
git branch --merged

# Delete merged branch (only possible if not HEAD):
git branch -d branchname

# Delete not merged branch:
git branch -D branch_to_delete

merge

git merge branchname

# Merge to master (only if fast forward):
git merge --ff-only branchname

Merge to master (force a new commit):
git merge --no-ff branchname

Stop merge (in case of conflicts):
git merge --abort

Undo local merge that hasn't been pushed yet:
git reset --hard origin/master

Merge only one specific commit:
git cherry-pick 073791e7
Merge Conflict Indicators Explanation

streams

source: Udacity - Version control with git

The editor has the following merge conflict indicators:

<<<<<<< HEAD everything below this line (until the next indicator) shows you what's on the current branch

||||||| merged common ancestors everything below this line (until the next indicator) shows you what the original lines were

======= is the end of the original lines, everything that follows (until the next indicator) is what's on the branch that's being merged in

>>>>>>> heading-update is the ending indicator of what's on the branch that's being merged in (in this case, the heading-update branch)

Stash

The git stash command takes your uncommitted changes (both staged and unstaged), saves them away for later use, and then reverts them from your working copy.

# Put in stash:
git stash save "Message"

# Show stash:
git stash list

# Show stash stats:
git stash show stash@{0}

# Show stash changes:
git stash show -p stash@{0}

# Use custom stash item and drop it:
git stash pop stash@{0}

# Use custom stash item and do not drop it:
git stash apply stash@{0}

# Use custom stash item and index:
git stash apply --index

# Create branch from stash:
git stash branch new_branch

# Delete custom stash item:
git stash drop stash@{0}

# Delete complete stash:
git stash clear

Other

# Delete untracked files (Dry run):
git clean -n

# Force to delete files or directories:
git clean -f

# Archive - Create a zip-archive
git archive --format zip --output filename.zip master

# Export/write custom log to a file:
git log --author=sven --all > log.txt

# Troubleshooting
Ignore files that have already been committed to a Git repository: http://stackoverflow.com/a/1139797/1815847

# Security
Hide Git on the web via .htaccess:
RedirectMatch 404 /\.git
(more info here: http://stackoverflow.com/a/17916515/1815847)
Large File Storage

Website: https://git-lfs.github.com/

Install: brew install git-lfs

Track .psd files: git lfs track ".psd" (init, add, commit and push as written above)