Skip to content

Instantly share code, notes, and snippets.

@staycebella
Last active December 27, 2015 02:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save staycebella/7253987 to your computer and use it in GitHub Desktop.
Save staycebella/7253987 to your computer and use it in GitHub Desktop.
Git Commands that come in handy
http://git-scm.com/book/en/Git-Branching-What-a-Branch-Is
Branching
What a Branch Is
To really understand the way Git does branching, we need to take a step back and examine how Git stores its data. As you may remember from Chapter 1, Git doesn’t store data as a series of changesets or deltas, but instead as a series of snapshots.
When you commit in Git, Git stores a commit object that contains a pointer to the snapshot of the content you staged, the author and message metadata, and zero or more pointers to the commit or commits that were the direct parents of this commit: zero parents for the first commit, one parent for a normal commit, and multiple parents for a commit that results from a merge of two or more branches.
To visualize this, let’s assume that you have a directory containing three files, and you stage them all and commit. Staging the files checksums each one (the SHA-1 hash we mentioned in Chapter 1), stores that version of the file in the Git repository (Git refers to them as blobs), and adds that checksum to the staging area:
$ git add README test.rb LICENSE
$ git commit -m 'initial commit of my project'
Running git commit checksums all project directories and stores them as tree objects in the Git repository. Git then creates a commit object that has the metadata and a pointer to the root project tree object so it can re-create that snapshot when needed.
Your Git repository now contains five objects: one blob for the contents of each of your three files, one tree that lists the contents of the directory and specifies which file names are stored as which blobs, and one commit with the pointer to that root tree and all the commit metadata. Conceptually, the data in your Git repository looks something like Figure 3-1.
If you make some changes and commit again, the next commit stores a pointer to the commit that came immediately before it. After two more commits, your history might look something like Figure 3-2.
A branch in Git is simply a lightweight movable pointer to one of these commits. The default branch name in Git is master. As you initially make commits, you’re given a master branch that points to the last commit you made. Every time you commit, it moves forward automatically.
What happens if you create a new branch? Well, doing so creates a new pointer for you to move around. Let’s say you create a new branch called testing. You do this with the git branch command:
$ git branch testing
This creates a new pointer at the same commit you’re currently on (see Figure 3-4).
How does Git know what branch you’re currently on? It keeps a special pointer called HEAD. Note that this is a lot different than the concept of HEAD in other VCSs you may be used to, such as Subversion or CVS. In Git, this is a pointer to the local branch you’re currently on. In this case, you’re still on master. The git branch command only created a new branch — it didn’t switch to that branch (see Figure 3-5).
To switch to an existing branch, you run the git checkout command. Let’s switch to the new testing branch:
$ git checkout testing
This moves HEAD to point to the testing branch (see Figure 3-6).
What is the significance of that? Well, let’s do another commit:
$ vim test.rb
$ git commit -a -m 'made a change'
This is interesting, because now your testing branch has moved forward, but your master branch still points to the commit you were on when you ran git checkout to switch branches. Let’s switch back to the master branch:
$ git checkout master
That command did two things. It moved the HEAD pointer back to point to the master branch, and it reverted the files in your working directory back to the snapshot that master points to. This also means the changes you make from this point forward will diverge from an older version of the project. It essentially rewinds the work you’ve done in your testing branch temporarily so you can go in a different direction.
Let’s make a few changes and commit again:
$ vim test.rb
$ git commit -a -m 'made other changes'
Now your project history has diverged (see Figure 3-9). You created and switched to a branch, did some work on it, and then switched back to your main branch and did other work. Both of those changes are isolated in separate branches: you can switch back and forth between the branches and merge them together when you’re ready. And you did all that with simple branch and checkout commands.
Because a branch in Git is in actuality a simple file that contains the 40 character SHA-1 checksum of the commit it points to, branches are cheap to create and destroy. Creating a new branch is as quick and simple as writing 41 bytes to a file (40 characters and a newline).
http://git-scm.com/docs/git-rebase
Rebase
Forward-port local commits to the updated upstream head
With the rebase command, you can take all the changes that were committed on one branch and replay them on another one.
In case of conflict, git rebase will stop at the first problematic commit and leave conflict markers in the tree. You can use git diff to locate the markers (<<<<<<) and make edits to resolve the conflict. For each file you edit, you need to tell Git that the conflict has been resolved, typically this would be done with
git add <filename>
After resolving the conflict manually and updating the index with the desired resolution, you can continue the rebasing process with
git rebase --continue
Alternatively, you can undo the git rebase with
git rebase --abort
EXAMPLES:
In this example, you’d run the following:
$ git checkout experiment
$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: added staged command
It works by going to the common ancestor of the two branches (the one you’re on and the one you’re rebasing onto), getting the diff introduced by each commit of the branch you’re on, saving those diffs to temporary files, resetting the current branch to the same commit as the branch you are rebasing onto, and finally applying each change in turn.
OPTIONS
--onto <newbase>
Starting point at which to create the new commits. If the --onto option is not specified, the starting point is <upstream>. May be any valid commit, and not just an existing branch name.
As a special case, you may use "A\...B" as a shortcut for the merge base of A and B if there is exactly one merge base. You can leave out at most one of A and B, in which case it defaults to HEAD.
<upstream>
Upstream branch to compare against. May be any valid commit, not just an existing branch name. Defaults to the configured upstream for the current branch.
<branch>
Working branch; defaults to HEAD.
--continue
Restart the rebasing process after having resolved a merge conflict.
--abort
Abort the rebase operation and reset HEAD to the original branch. If <branch> was provided when the rebase operation was started, then HEAD will be reset to <branch>. Otherwise HEAD will be reset to where it was when the rebase operation was started.
--keep-empty
Keep the commits that do not change anything from its parents in the result.
--skip
Restart the rebasing process by skipping the current patch.
--edit-todo
Edit the todo list during an interactive rebase.
-m
--merge
Use merging strategies to rebase. When the recursive (default) merge strategy is used, this allows rebase to be aware of renames on the upstream side.
Note that a rebase merge works by replaying each commit from the working branch on top of the <upstream> branch. Because of this, when a merge conflict happens, the side reported as ours is the so-far rebased series, starting with <upstream>, and theirs is the working branch. In other words, the sides are swapped.
-s <strategy>
--strategy=<strategy>
Use the given merge strategy. If there is no -s option git merge-recursive is used instead. This implies --merge.
Because git rebase replays each commit from the working branch on top of the <upstream> branch using the given strategy, using the ours strategy simply discards all patches from the <branch>, which makes little sense.
-X <strategy-option>
--strategy-option=<strategy-option>
Pass the <strategy-option> through to the merge strategy. This implies --merge and, if no strategy has been specified, -s recursive. Note the reversal of ours and theirs as noted above for the -m option.
-q
--quiet
Be quiet. Implies --no-stat.
-v
--verbose
Be verbose. Implies --stat.
--stat
Show a diffstat of what changed upstream since the last rebase. The diffstat is also controlled by the configuration option rebase.stat.
-n
--no-stat
Do not show a diffstat as part of the rebase process.
--no-verify
This option bypasses the pre-rebase hook. See also githooks(5).
--verify
Allows the pre-rebase hook to run, which is the default. This option can be used to override --no-verify. See also githooks(5).
-C<n>
Ensure at least <n> lines of surrounding context match before and after each change. When fewer lines of surrounding context exist they all must match. By default no context is ever ignored.
-f
--force-rebase
Force the rebase even if the current branch is a descendant of the commit you are rebasing onto. Normally non-interactive rebase will exit with the message "Current branch is up to date" in such a situation. Incompatible with the --interactive option.
You may find this (or --no-ff with an interactive rebase) helpful after reverting a topic branch merge, as this option recreates the topic branch with fresh commits so it can be remerged successfully without needing to "revert the reversion" (see the revert-a-faulty-merge How-To for details).
--ignore-whitespace
--whitespace=<option>
These flag are passed to the git apply program (see git-apply(1)) that applies the patch. Incompatible with the --interactive option.
--committer-date-is-author-date
--ignore-date
These flags are passed to git am to easily change the dates of the rebased commits (see git-am(1)). Incompatible with the --interactive option.
-i
--interactive
Make a list of the commits which are about to be rebased. Let the user edit that list before rebasing. This mode can also be used to split commits (see SPLITTING COMMITS below).
-p
--preserve-merges
Instead of ignoring merges, try to recreate them.
This uses the --interactive machinery internally, but combining it with the --interactive option explicitly is generally not a good idea unless you know what you are doing (see BUGS below).
-x <cmd>
--exec <cmd>
Append "exec <cmd>" after each line creating a commit in the final history. <cmd> will be interpreted as one or more shell commands.
This option can only be used with the --interactive option (see INTERACTIVE MODE below).
You may execute several commands by either using one instance of --exec with several commands:
git rebase -i --exec "cmd1 && cmd2 && ..."
or by giving more than one --exec:
git rebase -i --exec "cmd1" --exec "cmd2" --exec ...
If --autosquash is used, "exec" lines will not be appended for the intermediate commits, and will only appear at the end of each squash/fixup series.
--root
Rebase all commits reachable from <branch>, instead of limiting them with an <upstream>. This allows you to rebase the root commit(s) on a branch. When used with --onto, it will skip changes already contained in <newbase> (instead of <upstream>) whereas without --onto it will operate on every change. When used together with both --onto and --preserve-merges, all root commits will be rewritten to have <newbase> as parent instead.
--autosquash
--no-autosquash
When the commit log message begins with "squash! ..." (or "fixup! ..."), and there is a commit whose title begins with the same ..., automatically modify the todo list of rebase -i so that the commit marked for squashing comes right after the commit to be modified, and change the action of the moved commit from pick to squash (or fixup). Ignores subsequent "fixup! " or "squash! " after the first, in case you referred to an earlier fixup/squash with git commit --fixup/--squash.
This option is only valid when the --interactive option is used.
If the --autosquash option is enabled by default using the configuration variable rebase.autosquash, this option can be used to override and disable this setting.
--[no-]autostash
Automatically create a temporary stash before the operation begins, and apply it after the operation ends. This means that you can run rebase on a dirty worktree. However, use with care: the final stash application after a successful rebase might result in non-trivial conflicts.
--no-ff
With --interactive, cherry-pick all rebased commits instead of fast-forwarding over the unchanged ones. This ensures that the entire history of the rebased branch is composed of new commits.
Without --interactive, this is a synonym for --force-rebase.
You may find this helpful after reverting a topic branch merge, as this option recreates the topic branch with fresh commits so it can be remerged successfully without needing to "revert the reversion" (see the revert-a-faulty-merge How-To for details).
http://git-scm.com/docs/git-reset
git-reset - Reset current HEAD to the specified state
SYNOPSIS
git reset [-q] [<tree-ish>] [--] <paths>...
git reset (--patch | -p) [<tree-ish>] [--] [<paths>...]
git reset [--soft | --mixed | --hard | --merge | --keep] [-q] [<commit>]
DESCRIPTION
In the first and second form, copy entries from <tree-ish> to the index. In the third form, set the current branch head (HEAD) to <commit>, optionally modifying index and working tree to match. The <tree-ish>/<commit> defaults to HEAD in all forms.
git reset [-q] [<tree-ish>] [--] <paths>...
This form resets the index entries for all <paths> to their state at <tree-ish>. (It does not affect the working tree, nor the current branch.)
This means that git reset <paths> is the opposite of git add <paths>.
After running git reset <paths> to update the index entry, you can use git-checkout(1) to check the contents out of the index to the working tree. Alternatively, using git-checkout(1) and specifying a commit, you can copy the contents of a path out of a commit to the index and to the working tree in one go.
git reset (--patch | -p) [<tree-ish>] [--] [<paths>...]
Interactively select hunks in the difference between the index and <tree-ish> (defaults to HEAD). The chosen hunks are applied in reverse to the index.
This means that git reset -p is the opposite of git add -p, i.e. you can use it to selectively reset hunks. See the “Interactive Mode” section of git-add(1) to learn how to operate the --patch mode.
git reset [<mode>] [<commit>]
This form resets the current branch head to <commit> and possibly updates the index (resetting it to the tree of <commit>) and the working tree depending on <mode>. If <mode> is omitted, defaults to "--mixed". The <mode> must be one of the following:
--soft
Does not touch the index file nor the working tree at all (but resets the head to <commit>, just like all modes do). This leaves all your changed files "Changes to be committed", as git status would put it.
--mixed
Resets the index but not the working tree (i.e., the changed files are preserved but not marked for commit) and reports what has not been updated. This is the default action.
--hard
Resets the index and working tree. Any changes to tracked files in the working tree since <commit> are discarded.
--merge
Resets the index and updates the files in the working tree that are different between <commit> and HEAD, but keeps those which are different between the index and working tree (i.e. which have changes which have not been added). If a file that is different between <commit> and the index has unstaged changes, reset is aborted.
In other words, --merge does something like a git read-tree -u -m <commit>, but carries forward unmerged index entries.
--keep
Resets index entries and updates files in the working tree that are different between <commit> and HEAD. If a file that is different between <commit> and HEAD has local changes, reset is aborted.
If you want to undo a commit other than the latest on a branch, git-revert(1) is your friend.
OPTIONS
-q
--quiet
Be quiet, only report errors.
EXAMPLES
Undo add
$ edit <1>
$ git add frotz.c filfre.c
$ mailx <2>
$ git reset <3>
$ git pull git://info.example.com/ nitfol <4>
You are happily working on something, and find the changes in these files are in good order. You do not want to see them when you run "git diff", because you plan to work on other files and changes with these files are distracting.
Somebody asks you to pull, and the changes sounds worthy of merging.
However, you already dirtied the index (i.e. your index does not match the HEAD commit). But you know the pull you are going to make does not affect frotz.c nor filfre.c, so you revert the index changes for these two files. Your changes in working tree remain there.
Then you can pull and merge, leaving frotz.c and filfre.c changes still in the working tree.
Undo a commit and redo
$ git commit ...
$ git reset --soft HEAD^ <1>
$ edit <2>
$ git commit -a -c ORIG_HEAD <3>
This is most often done when you remembered what you just committed is incomplete, or you misspelled your commit message, or both. Leaves working tree as it was before "reset".
Make corrections to working tree files.
"reset" copies the old head to .git/ORIG_HEAD; redo the commit by starting with its log message. If you do not need to edit the message further, you can give -C option instead.
See also the --amend option to git-commit(1).
Undo a commit, making it a topic branch
$ git branch topic/wip <1>
$ git reset --hard HEAD~3 <2>
$ git checkout topic/wip <3>
You have made some commits, but realize they were premature to be in the "master" branch. You want to continue polishing them in a topic branch, so create "topic/wip" branch off of the current HEAD.
Rewind the master branch to get rid of those three commits.
Switch to "topic/wip" branch and keep working.
Undo commits permanently
$ git commit ...
$ git reset --hard HEAD~3 <1>
The last three commits (HEAD, HEAD^, and HEAD~2) were bad and you do not want to ever see them again. Do not do this if you have already given these commits to somebody else. (See the "RECOVERING FROM UPSTREAM REBASE" section in git-rebase(1) for the implications of doing so.)
Undo a merge or pull
$ git pull <1>
Auto-merging nitfol
CONFLICT (content): Merge conflict in nitfol
Automatic merge failed; fix conflicts and then commit the result.
$ git reset --hard <2>
$ git pull . topic/branch <3>
Updating from 41223... to 13134...
Fast-forward
$ git reset --hard ORIG_HEAD <4>
Try to update from the upstream resulted in a lot of conflicts; you were not ready to spend a lot of time merging right now, so you decide to do that later.
"pull" has not made merge commit, so "git reset --hard" which is a synonym for "git reset --hard HEAD" clears the mess from the index file and the working tree.
Merge a topic branch into the current branch, which resulted in a fast-forward.
But you decided that the topic branch is not ready for public consumption yet. "pull" or "merge" always leaves the original tip of the current branch in ORIG_HEAD, so resetting hard to it brings your index file and the working tree back to that state, and resets the tip of the branch to that commit.
git branch <new_branch_name>
git checkout <new_branch_name>
+++Do the work; when done; continue:
git status
git add .
git commit -am "<commit_message>"
git pull --rebase up master
git push origin android_video
http://git-scm.com/docs/git-push
Note about fast-forwards
When an update changes a branch (or more in general, a ref) that used to point at commit A to point at another commit B, it is called a fast-forward update if and only if B is a descendant of A.
In a fast-forward update from A to B, the set of commits that the original commit A built on top of is a subset of the commits the new commit B builds on top of. Hence, it does not lose any history.
In contrast, a non-fast-forward update will lose history. For example, suppose you and somebody else started at the same commit X, and you built a history leading to commit B while the other person built a history leading to commit A. The history looks like this:
B
/
---X---A
Further suppose that the other person already pushed changes leading to A back to the original repository from which you two obtained the original commit X.
The push done by the other person updated the branch that used to point at commit X to point at commit A. It is a fast-forward.
But if you try to push, you will attempt to update the branch (that now points at A) with commit B. This does not fast-forward. If you did so, the changes introduced by commit A will be lost, because everybody will now start building on top of B.
The command by default does not allow an update that is not a fast-forward to prevent such loss of history.
If you do not want to lose your work (history from X to B) nor the work by the other person (history from X to A), you would need to first fetch the history from the repository, create a history that contains changes done by both parties, and push the result back.
You can perform "git pull", resolve potential conflicts, and "git push" the result. A "git pull" will create a merge commit C between commits A and B.
B---C
/ /
---X---A
Updating A with the resulting merge commit will fast-forward and your push will be accepted.
Alternatively, you can rebase your change between X and B on top of A, with "git pull --rebase", and push the result back. The rebase will create a new commit D that builds the change between X and B on top of A.
B D
/ /
---X---A
Again, updating A with this commit will fast-forward and your push will be accepted.
There is another common situation where you may encounter non-fast-forward rejection when you try to push, and it is possible even when you are pushing into a repository nobody else pushes into. After you push commit A yourself (in the first picture in this section), replace it with "git commit --amend" to produce commit B, and you try to push it out, because forgot that you have pushed A out already. In such a case, and only if you are certain that nobody in the meantime fetched your earlier commit A (and started building on top of it), you can run "git push --force" to overwrite it. In other words, "git push --force" is a method reserved for a case where you do mean to lose history.
Examples
git push
Works like git push <remote>, where <remote> is the current branch's remote (or origin, if no remote is configured for the current branch).
git push origin
Without additional configuration, works like git push origin :.
The default behavior of this command when no <refspec> is given can be configured by setting the push option of the remote, or the push.default configuration variable.
For example, to default to pushing only the current branch to origin use git config remote.origin.push HEAD. Any valid <refspec> (like the ones in the examples below) can be configured as the default for git push origin.
git push origin :
Push "matching" branches to origin. See <refspec> in the OPTIONS section above for a description of "matching" branches.
git push origin master
Find a ref that matches master in the source repository (most likely, it would find refs/heads/master), and update the same ref (e.g. refs/heads/master) in origin repository with it. If master did not exist remotely, it would be created.
git push origin HEAD
A handy way to push the current branch to the same name on the remote.
git push mothership master:satellite/master dev:satellite/dev
Use the source ref that matches master (e.g. refs/heads/master) to update the ref that matches satellite/master (most probably refs/remotes/satellite/master) in the mothership repository; do the same for dev and satellite/dev.
This is to emulate git fetch run on the mothership using git push that is run in the opposite direction in order to integrate the work done on satellite, and is often necessary when you can only make connection in one way (i.e. satellite can ssh into mothership but mothership cannot initiate connection to satellite because the latter is behind a firewall or does not run sshd).
After running this git push on the satellite machine, you would ssh into the mothership and run git merge there to complete the emulation of git pull that were run on mothership to pull changes made on satellite.
git push origin HEAD:master
Push the current branch to the remote ref matching master in the origin repository. This form is convenient to push the current branch without thinking about its local name.
git push origin master:refs/heads/experimental
Create the branch experimental in the origin repository by copying the current master branch. This form is only needed to create a new branch or tag in the remote repository when the local name and the remote name are different; otherwise, the ref name on its own will work.
git push origin :experimental
Find a ref that matches experimental in the origin repository (e.g. refs/heads/experimental), and delete it.
git push origin +dev:master
Update the origin repository's master branch with the dev branch, allowing non-fast-forward updates. This can leave unreferenced commits dangling in the origin repository. Consider the following situation, where a fast-forward is not possible:
o---o---o---A---B origin/master
\
X---Y---Z dev
The above command would change the origin repository to
A---B (unnamed branch)
/
o---o---o---X---Y---Z master
Commits A and B would no longer belong to a branch with a symbolic name, and so would be unreachable. As such, these commits would be removed by a git gc command on the origin repository.
In depth notes regarding commands used after I accidently reset too far back and my PR had 30 other commits besides my own.
git reflog
Reflog is a mechanism to record when the tip of branches are updated. This command is to manage the information recorded in it.
The reflog is useful in various Git commands, to specify the old value of a reference. For example, HEAD@{2} means "where HEAD used to be two moves ago", master@{one.week.ago} means "where master used to point to one week ago", and so on.
git reset --hard HEAD@{36}
Undo the previous 36 moves; where master used to point to before messing up
git status
Check to see if my files are present so I can continue working/updating them
git rebase --continue
integrate changes from one branch into another
git branch <type_branch_name_to_create>
Creates a new pointer at the same commit you’re currently on. Every time you commit, it moves forward automatically.
git checkout <branch_name>
Switches between branches. That command did two things. It moved the HEAD pointer back to point to the master branch, and it reverted the files in your working directory back to the snapshot that master points to. This also means the changes you make from this point forward will diverge from an older version of the project. It essentially rewinds the work you’ve done in your testing branch temporarily so you can go in a different direction.
git fetch <repo>
Download objects and refs from another repository
EXAMPLE
Update the remote-tracking branches:
$ git fetch origin
The above command copies all branches from the remote refs/heads/ namespace and stores them to the local refs/remotes /origin/ namespace, unless the branch.<name>.fetch option is used to specify a non-default refspec.
git pull --rebase up master
Fetch from and integrate with another repository or a local branch
Incorporates changes from a remote repository into the current branch. In its default mode, git pull is shorthand for git fetch followed by git merge FETCH_HEAD.
More precisely, git pull runs git fetch with the given parameters and calls git merge to merge the retrieved branch heads into the current branch. With --rebase, it runs git rebase instead of git merge.
git push origin <branch_name>
Update remote refs along with associated objects (pushing local changes to forked repo)
gitpush -f
Must be used after rewriting history (e.g. git reset)
Usually, the command refuses to update a remote ref that is not an ancestor of the local ref used to overwrite it. This flag disables the check. This can cause the remote repository to lose commits; use it with care. Note that --force applies to all the refs that are pushed, hence using it with push.default set to matching or with multiple push destinations configured with remote.*.push may overwrite refs other than the current branch (including local refs that are strictly behind their remote counterpart). To force a push to only one branch, use a + in front of the refspec to push (e.g git push origin +master to force a push to the master branch). See the <refspec>... section above for details.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment