Git: Difference between revisions
Line 629: | Line 629: | ||
=== [http://www.kernel.org/pub/software/scm/git/docs/git-reset.html git-reset] === |
=== [http://www.kernel.org/pub/software/scm/git/docs/git-reset.html git-reset] === |
||
''git reset'' has two purposes: |
|||
''git reset'' resets the current <tt>HEAD</tt> to the new state (hence also moving branch tip if you are currently on a branch). There are 3 different reset modes: |
|||
<ul> |
<ul> |
||
<li> ''' |
<li> '''<tt>commit</tt> ''paths...'' → ''Index''''' <br/>Copy ''paths...'' from <tt>HEAD</tt> (or given ''commit'') to ''index'' (working tree left ''unchanged''), or<li> |
||
<li> '''branch head → <tt>commit</tt>''' <br/>Move current branch head to <tt>HEAD</tt> (or given ''commit''), and optionally update ''index'' and/or ''working tree''.</li> |
|||
<li> '''mixed''' (default)<br/>Like ''soft'', but also reset the index (working tree files are left untouched). This actually clear the index from any staged changes.</li> |
|||
Modes are: |
|||
<li> '''hard'''<br/> Like ''mixed'', but also erases all changes in the working tree, so that it matches the contents of the new <tt>HEAD</tt>. This is a dangerous command, so better use some alternatives that avoid data loss, like: |
|||
{| class=wikitable |
|||
|- |
|||
!mode!!index!!working tree!!preserve local changes |
|||
|- align="left" |
|||
|'''<tt>--soft</tt>''' |
|||
| |
|||
| |
|||
|{{green|Yes}} |
|||
|- align="left" |
|||
|'''<tt>--mixed</tt>''' (default) |
|||
|reset |
|||
|| |
|||
|{{green|Yes}} |
|||
|- align="left" |
|||
|'''<tt>--hard</tt>''' |
|||
|reset |
|||
|{{red|'''reset'''}} |
|||
|{{red|'''No'''}} |
|||
|- align="left" |
|||
|'''<tt>--merge</tt>''' |
|||
|reset |
|||
|{{green|'''keep'''}} if ''idx'' = ''commit'', else<br/>{{red|'''reset'''}} if ''idx'' = ''tree'', else<br/>'''abort''' |
|||
|{{red|'''No'''}}<br/>(fully staged changes are lost) |
|||
|- align="left" |
|||
|'''<tt>--keep</tt>''' |
|||
|reset |
|||
|{{green|'''keep'''}} if ''HEAD'' = ''commit'', else<br/>{{red|'''reset'''}} if ''HEAD'' = ''tree'', else<br/>'''abort''' |
|||
|{{green|'''Yes'''}} |
|||
|} |
|||
⚫ | |||
⚫ | |||
⚫ | |||
* {{red|Caution!}} — Don't move the head of a branch if changes have been already published, or this will create many merge conflicts. |
|||
* {{red|Caution!}} — ''git reset --hard'' is a dangerous command. Prefer one of its alternatives, like: |
|||
{| |
{| |
||
|- valign="top" |
|- valign="top" |
||
| |
| |
||
Commit any changes first |
<center>Commit any changes first</center> |
||
<source lang="bash"> |
<source lang="bash"> |
||
git commit -a -m "snapshot WIP" |
git commit -a -m "snapshot WIP" |
||
Line 643: | Line 678: | ||
</source> |
</source> |
||
| |
| |
||
Stash the changes |
<center>Stash the changes</center> |
||
<source lang="bash"> |
<source lang="bash"> |
||
git stash |
git stash |
||
Line 652: | Line 687: | ||
</source> |
</source> |
||
| |
| |
||
Idem & don't change master too early |
<center>Idem & don't change master too early</center> |
||
<source lang="bash"> |
<source lang="bash"> |
||
git stash |
git stash |
||
Line 661: | Line 696: | ||
</source> |
</source> |
||
|} |
|} |
||
<li> |
|||
⚫ | |||
⚫ | |||
⚫ | |||
* {{red|Caution!}} — ''git reset'' moves the tip of the current branch (when HEAD is a ref to a branch). Don't do this on changes that have been published. |
|||
Revision as of 13:48, 21 September 2011
References
- Git Home
- Git Tutorial
- Git on Ubuntu
- Pro Git
- Git, from the bottom up
- Linux Greatest Invention
- Tech Talk: Linux Torvalds on git
- Git cheat sheet
- Remove Sensitive Data from Git
Example on how to usegit-filter-branch
to delete files and propagates this upstream.
Git on Windows
- http://superuser.com/questions/138669/why-is-tortoise-git-changing-my-file-permissions
Might requiregit config core.filemode false
- Using cygwin git instead of msysgit with TortoiseGit
Git SVN
Subversion integration
See Git SVN page
Introduction
Git Features:
- Reliability
- Performance
- Distributed
Distributed
Originally from BitKeeper. Other distributed SCM is Mercurial.
- No single repository. Everybody always has his own copy of the repository. The repository content is pulled from other people's repository.
- No politics, no commit access control. All work is always done locally, so there is no need to define such politics.
Reliability
Every change, file, directory, etc. is cryptographically hashed (sha1sum).
- Easy corruption detection. Any tampering to a file or directory content (either malicious or because of hardware failure) is immediately detected.
- Easy distribution. Moreover because the repository is distributed all over the place, it is very easy to repair a given repository. You only need to drop all broken objects, and get all missing objects from a remote copy.
Performance
Very fast commit. Local repository
Terminology and Concepts
- commit
- A commit is a snapshot of your working tree at some point in time. There are different ways to name a commit:
- branchname — a branch name is an alias for most recent commit on that branch
- tagname — similar to a branch alias, but that does not change in time
- HEAD — currently checked out commit
- c82a22c — the SHA-1 hash id of the commit (can be truncated as long as it remains unique)
- name^ — the parent of commit name
- name^^ — the grand-parent of commit name (and so on)
- name^2 — the 2nd parent of commit name (and so on)
- name~10 — the 10th ancestor of commit name (same as name^^^^^^^^^^)
- name:path — reference a specific file/directory in a given commit
- name^{tree} — reference the tree held by a commit
- name1..name2 — a commit range, i.e. all commits reachable from name2 back to, but no including, name1 (if either name is omitted, use HEAD instead)
- name1...name2 — refers to all commits referenced by name1 or name2, but not by both. For
git diff
, refers to all commits between name2 and the common ancestor of name1 and name2. - master.. — to review changes made to the current branch
- ..master — after a
fetch
, to review all changes occured since lastrebase
ormerge
- --since="2 weeks ago" — all commits since a certain date
- --until=”1 week ago” — all commits up to a certain date
- --grep=pattern — all commits whose commit message matches the regular expression pattern.
- --committer=pattern — all commits whose committer matches the pattern
- --author=pattern — all commits whose author matches the pattern
- --no-merges — all commits in a range that have only one pattern (i.e. ignore all merge commits)
- detached head
- When HEAD is no longer a reference to anything (like ref: refs/heads/branch), but instead contains the actual hash of a commit.
git checkout -b newbranch # To attach HEAD back on a new branch...
- hunk
- individual change within a file (basically a file diff output is made of a sequence of one or more hunks).
Install
Install the following essential packages:
- git-core — the main program
- git-gui — a gui front-end
Optionally install also:
- git-doc — documentation
- gitweb — Web interface
- ViewGit — Another web interface
- gitosis — Project management:
- tig — a text-mode repository browser interface to git and color pager.
tig # launch browser
git show | tig # Use as pager. Colorize output of git-show
- gitview — Git Repository browser
- gitg — a Git repository browser targeting Gtk+ / GNOME Version delivered with Lucid/Maverick is a very old one. Compile from the sources to get the latest version. Alternatively the repository ppa:pasgui/ppa contains a more recent version.
- qgit — A graphical interface to git repositories using QT
To install the repository, create a file /etc/apt/sources.list.d/pasgui-ppa-lucid.list (change lucid as necessary):
deb http://ppa.launchpad.net/pasgui/ppa/ubuntu lucid main
deb-src http://ppa.launchpad.net/pasgui/ppa/ubuntu lucid main
Then add the apt key:
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys F599ACE3
Configuration
References:
- Git Community Boot - Customizing Git
- Git handy feedback on command-line
Global per-user configuration settings are stored in file ~/.gitconfig
- Add color to git output for all commmands:
- Define author/email
- Add some frequently used aliases:
- Some handy scripts:
- git-wtf displays the state of your repository in a readable and easy-to-scan format
- To solve the issue of ugly fonts (not anti-aliased besides other uglyness), install tk8.5 and force alternatives for wish (see [1]):
git config --global color.ui true
git config --global user.name "Your Name"
git config --global user.email you@example.com
git config --global alias.st 'status'
git config --global alias.ci 'commit'
git config --global alias.co 'checkout'
git config --global alias.br 'branch'
git config --global alias.last 'log -1 HEAD'
# h like history
git config --global alias.h 'log --oneline --graph --decorate -45'
git config --global alias.ha 'log --oneline --graph --decorate -45 --all'
# l like log ;-)
git config --global alias.l 'log --pretty=tformat:\"%C(yellow)%h %Cblue%an %Cgreen%cr %Cred%d %Creset%s\" --graph -45'
git config --global alias.la 'log --pretty=tformat:\"%C(yellow)%h %Cblue%an %Cgreen%cr %Cred%d %Creset%s\" --graph -45 --all'
git config --global alias.dc 'diff --cached'
git config --global alias.wdiff 'diff --color-words'
git config --global alias.wshow 'show --color-words'
sudo apt-get install tk8.5
sudo update-alternatives --config wish
# select wish8.5
An alternative however is to use gitg.
How-To
Here we shall describe how to perform some tasks in Git.
Cloning to/from a Server using SSH
Reference: [2]
Clone a local repository to remote server griffin, through ssh. Repositories are all stored in a directory repositories/ in home directory of remote user git :
git clone --bare myproject myproject.git # Create a bare clone of your repository, if not available yet
scp -r myproject.git/ git@griffin:repositories/ # Copy the repository to server - requires SSH access
rm -rf myproject.git # Delete local bare clone
Now any other user that has SSH access to git@griffin may get a copy of that repository with
git clone git@griffin:repositories/myproject.git # Clone repository and create working tree in myproject/
Now, the user that created the repository at the first place can
- either delete his own repository and clone the remote one as any other user,
- or more safely, he can tell git to add the remote repository and set up tracking branch for master (see git-remote below).
See git-clone below for more details.
Cloning from a Server using SSH (limited access)
Reference: [3]
Say you have an SSH access to a server but git-core is not installed you you can't install it yourself (for instance on a shared hosting server). You can still use git but it requires some "hacking":
- First copy the executables from package git-core, directory /usr/bin to some directory on the server where you have write access (say private/bin). Note that somes files are actually symlinks: private/bin/: -rwxr-xr-x user webusers git* lrwxrwxrwx user webusers git-receive-pack -> git* -rwxr-xr-x user webusers git-shell* lrwxrwxrwx user webusers git-upload-archive -> git* -rwxr-xr-x user webusers git-upload-pack*
- Clone from the server using -u command-line switch:
- Edit the local myproject/.git/config file to add the lines marked with a +:
[remote "origin"] fetch = +refs/heads/*:refs/remotes/origin/* url = user@server:private/git/myproject.git + uploadpack = /path/to/private/bin/git-upload-pack + receivepack = /path/to/private/bin/git-receive-pack
git clone -u </path/to/private/bin/git-upload-pack> user@server:private/git/myproject.git
Mirroring
Reference: [4]
Cloning from a Server using git: Protocol over a Proxy
The referenced links propose some script. Here another variant. Add this script to your path (say ~/bin/proxygit):
#!/bin/bash
# proxygit - git through http proxy
#
# Usage: proxygit [options] COMMAND [ARGS]
# Setup Git HTTP proxy variable GIT_PROXY_COMMAND and call git with the given parameters.
# The proxy settings are read from env variable $http_proxy
#
# Note:
# - Requires package socat
# - $GIT_PROXY_COMMAND must not be defined
if [ -n "$GIT_PROXY_COMMAND" ]; then
PROXY=$(echo $http_proxy|sed -r 's!(http://)?([^/:]*):([0-9]*)/?!\2!')
PROXYPORT=$(echo $http_proxy|sed -r 's!(http://)?([^/:]*):([0-9]*)/?!\3!')
exec /usr/bin/socat - "PROXY:$PROXY:$1:$2,proxyport=$PROXYPORT"
else
export GIT_PROXY_COMMAND="$0"
exec git "$@"
fi
Work with local and remote branches
Let's assume you have already a remote repository setup, like you would obtain if you clone a remote repository:
git clone user@server:repositories/project.git
By issuing this command, git will automatically:
- Create a local repository, clone of the remote one
- Call the remote repository origin
- Create a local branch master, and
- Configure it to track the branch master on origin. That remote branch is called locally origin/master.
The following commands can be used to create, track, delete local and remote branches.
# CREATE a local branch
git branch newbranch # Create a new local branch 'newbranch'
# ... use "git checkout newbranch" to check it out
# CREATE & CHECKOUT a local branch
git checkout -b newbranch # Create a new local branch 'newbranch' and check it out
# PUBLISH a local branch (for TRACKing)
# git push [remotename] [localbranch]:[remotebranch]
git push origin serverfix # Push a local branch 'serverfix' to remote (create it if necessary)
git push -u origin serverfix # ... same but also set upstream reference for TRACKING
git push -u origin serverfix:serverfix # ... same as above
git push -u origin serverfix:coolfix # ... same but call the branch 'coolfix' on the remote
# TRACK a remote branch
git branch --track sf origin/serverfix # Create a local branch 'sf' that tracks remote branch 'serverfix'
git branch --set-upstream sf origin/serverfix # ... same, but when local branch 'sf' already exists
# TRACK & CHECKOUT a remote branch
git checkout --track origin/serverfix # Checkout a new local branch 'serverfix' to track remote branch 'serverfix'
# (remember that this branch is called locally 'origin/serverfix')
git checkout -b sf origin/serverfix # ... same as above, but the local branch is named 'sf'
# FETCH / UPDATE from remote
git fetch
git fetch --prune # After fetching, remove any remote tracking branches that no longer exist on the remote
# FETCH from & MERGE with remote
git pull
git pull --prune # After fetching, remove any remote tracking branches that no longer exist on the remote
# DELETE a branch
git branch -d sf # Delete local branch 'sf'
git branch -d -r origin/serverfix # Delete remote tracking branch 'serverfix'
git push origin :serverfix # Delete branch 'serverfix' on 'origin'
# (basically this means push nothing to remote 'serverfix')
In summary:
- Use
git branch
to create, update, delete branches on the local repository. - Use
git checkout
to checkout (possibly new) local branches. - Use
git push
to update the remote, possibly publishing or deleting branches.
Define a diff textconv filter
This applies a diff filter, but only for git diff
and git log
commands:
- Edit file ~/.gitattributes, add (! no quotes around the diff parameter)
*.adr diff=bookmarks_adr
- Edit file ~/.gitconfig, add
[diff "bookmarks_adr"]
textconv = sed -r '/NAME=|URL=|SHORT NAME=/!d'
Commands
Here we'll summarize how to use some of the Git commands
Frequently used & common command options
Option | Valid for | Description |
---|---|---|
-p, -u, --patch | log | Generate patch |
-b | log, diff, show | Ignore whitespace changes |
--oneline | log, show | Show commit # + description on oneline |
-M | log, diff, show | Find renames |
--summary | log, diff, show | Show summary of changes (file creation, rename...) |
--name-status | log, diff, show | List file name and type of change (Add, Copied, Deleted, Modified, Renamed) |
git-add
git-add adds file contents to the index
git add -A # Stage all modified AND new files in current directory and recursively
git-blame
git blame -w file # Blame file, ignore whitespaces
git blame file | sort | uniq -w 8 | egrep -o "^.{8}" # Show list of commits that produced file
# (! actually a subset of git log)
git-branch
git-branch lists, creates, or deletes branches
# CREATE a local branch
git branch newbranch # Create a new local branch 'newbranch'
# ... use "git checkout newbranch" to check it out
# TRACK a remote branch
git branch --track sf origin/serverfix # Create a local branch 'sf' that tracks remote branch 'serverfix'
git branch --set-upstream sf origin/serverfix # ... same, but when local branch 'sf' already exists
# DELETE a branch
git branch -d sf # Delete local branch 'sf'
git branch -d -r origin/serverfix # Delete remote tracking branch 'serverfix' (see remark below)
git remote prune origin # Prune all state remote tracking branch
# MOVE a branch
git branch -f branch commit # Move tip of an existing branch to a different commit
Note:
- You can also track local branch (
git branch -t local1 local2
), but is it useful? - Deleting remote tracking branch (
git branch -d -r
) on the local repository only makes sense if the remote branch has been deleted on the remote, or if git-fetch has been configured not to import that branch anymore. So the best is simply to prune remote tracking branches automatically:
git remote prune origin # Remove all remote tracking branches that no longer exist on the remote (i.e. stale branches)
git fetch --prune # After fetching, remove all remote tracking branches that no longer exist on the remote
git-checkout
git-checkout checkouts a branch or paths to the working tree.
# CHECKOUT a local branch
git checkout mybranch # Checkout an existing branch
# CREATE & CHECKOUT a local branch
git checkout -b newbranch # Create a new local branch 'newbranch' and check it out
# TRACK & CHECKOUT a remote branch
git checkout --track origin/serverfix # Checkout a new local branch 'serverfix' to track remote branch 'serverfix'
# (remember that this branch is called locally 'origin/serverfix')
git checkout -b sf origin/serverfix # ... same as above, but the local branch is named 'sf'
# DISCARD local changes (.. checkout file as in local repository)
git checkout -- <file> # DISCARD changes in file <file> in the working tree
git-clone
git-clone is mainly used to create a local copy of a remote repository, or to create a bare repository (i.e. one without a working tree) for remote storage:
- Clone a remote repository:
- Create a bare repository for remote storage:
git clone git@griffin:repositories/myproject.git # Clone repository and create working tree in myproject/
git clone --bare myproject myproject.git # Create a bare clone of your repository, if not available yet
scp -r myproject.git/ git@griffin:repositories/ # Copy the repository to server - requires SSH access
rm -rf myproject.git # Delete local bare clone
The command git clone /dir/repo/project.git
is identical to running the following commands:
git init # Create an empty repo
git remote add -f origin /dir/repo/project.git # Add a remote repo called 'origin' and fetch
git set-head -a # Set default remote branch for remote 'origin' automatically
git checkout --track origin/master # Create a tracking branch 'master', and update working tree
Another equivalent option for last command is git checkout -b origin origin/master
(since the start-point is remote, git creates a tracking branch).
Some variants:
- To get a local copy of a remote repository, but without changing the working tree (i.e. keeping all local changes), just change the last command to:
git init
git remote add -f origin -m master /dir/repo/project.git
git branch --track master origin/master # Branch 'master' set up to track remote branch 'master' from 'origin'
- To merge remote branch locally, but without creating a tracking branch, change the last command to :
# ...
git merge origin/master # Merge
git-commit
git commit -m "commit message" # Gives immediately the commit message on the command-line
git commit -a # Add all changes and commit in one pass
git commit --amend # Amend tip current branch (message, add some files) - also for merge commits
Add the following line to your file ~/.gitconfig:
git config --global alias.ci 'commit'
Now you can use ci instead of commit:
git ci -m "commit message"
git-diff
git-diff shows changes between commits, commit and working tree, etc
Sample of useful options (some can be combined):
git diff --stat <commit> # Show changes summary as a graph (with + and -)
git diff -b <commit> # Ignore whitespaces
git diff -w <commit> # Ignore all whitespaces (remove them completely)
git diff --name-only <commit> # Only list names of changed files
git diff --relative[=<dir>] <commit> # Restrict changes to current (or given) directory and show relative pathname
git diff -M --summary <commit> # Show summary of changes (incl. file creation), find renames
git-fetch
git-fetch downloads objects and refs from another repository.
git fetch -p # After fetching, remove any remote-tracking branches which no longer exist on the remote
# (see also git remote prune origin)
git-filter-branch
git-filter-branch rewrites branches.
This command can be used for instance to rewrite / rebase a branch while changing the author/committer name/email (see [7] and [8]):
# Set the committer name to author name, for instance after a rebase
git filter-branch --commit-filter '
export GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME";
export GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL";
export GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE";
git commit-tree "$@"' -- basecommit..HEAD
# Change the author/committer name for specific commit (e.g. if wrong email was used):
git filter-branch --commit-filter '
if [ "$GIT_COMMITTER_NAME" = "<Old Name>" ];
then
GIT_COMMITTER_NAME="<New Name>";
GIT_AUTHOR_NAME="<New Name>";
GIT_COMMITTER_EMAIL="<New Email>";
GIT_AUTHOR_EMAIL="<New Email>";
git commit-tree "$@";
else
git commit-tree "$@";
fi' HEAD
Note that basecommit..HEAD
can easily be changed to other commit specification, like --since="1 year ago"
Another example to remove sensitive files from repository (from [9]):
# Delete the file(s)
git clone git@github.com:defunkt/github-gem.git
cd github-gem/
git filter-branch --index-filter 'git rm --cached --ignore-unmatch pattern1 pattern2 ...' HEAD
# .... Add --tag-name-filter "cat" to keep tags - will overwrite existing tags!
# Push to origin
git push origin master --force
# Force cleanup
rm -rf .git/refs/original/
git reflog expire --expire=now --all
git gc --prune=now
git gc --aggressive --prune=now
git-grep
git-grep prints lines matching a pattern (see also Git Book)
# Find all occurences of pattern in all files committed since last year
for i in $(git log --oneline --all --graph --since="1 year ago" | egrep -o " [a-h0-9]{7} "); do git grep pattern $i; done
git-log
git-log shows commit logs.
git log # Standard history log
git log -5 # Limit to 5 commits
git log -- file # List commits affecting file
git log -p -- file # History log, show patch/diff for file
git log -p -M -- file # ... idem, but find also renames
git log --summary -M -- file # Condensed summary with renames
git log --stat -1 # Show diff-stat for last commit
git log -w master.. --stat -- $(find . -name "*.java") # Show all changes to java files since branching off master
Some handy aliases:
[alias]
# h like history
h = log --oneline --graph --decorate -45
ha = log --oneline --graph --decorate -45 --all
# l like log ;-)
l = log --pretty=tformat:\"%C(yellow)%h %Cblue%an %Cgreen%cr %Cred%d %Creset%s\" --graph -45
la = log --pretty=tformat:\"%C(yellow)%h %Cblue%an %Cgreen%cr %Cred%d %Creset%s\" --graph -45 --all
Some advanced queries:
git l --follow -- file # Show all commits that modified file (incl. renames)
# (includes commits that deleted lines, unlike blame)
git log -b --follow -p -- file # ... same, show patch and ignore blanks
CAUTION! — git log
will not show any differences for merge commits! To fix that, use options -m
, -c
or --cc
(see [10]):
git log -p <commit> # SHOW NOTHING!!!
git log -p -m <commit> # Show 2 diffs, one for each parent
git log -p -c <commit> # ... Same as above, but simultaneously
git log -p --cc <commit> # ... Same as above, but remove hunks that are identical to one of the parent
CAUTION! — git log --oneline --graph
output can be misleading! Overlooking the graph may give the impression that a given commit is in some branch where actually it is in another branch. Look at the asterisks (*) carefully!. Example:
* | 2dbfa34 frq05215 4 weeks ago Integration implementation... * | cd1d5a7 beq03416 4 weeks ago Merged head of trunk into... |\ \ * | | 84fb520 frq05215 4 weeks ago Integration implementation... # This commit is not in same branch as 0eef5c7! | | | * 0eef5c7 beq03416 4 weeks ago relaxed the rate adaptation... | | |/ | | * b998ebd nxp12661 4 weeks ago [PT_TLMT2086] Changed bool_t type into J9bool_t...
git-pull
git-pull fetches from and merges with another repository or a local branch.
git pull -p # After fetching, remove any remote-tracking branches which no longer exist on the remote
# (see also git remote prune)
git-push
git push updates remote refs along with associated objects. In layman english, git push basically pushes changes to the remote repository, possibly creating, updating, deleting branches, objects or references.
# PUBLISH a local branch (for TRACKing)
# git push [remotename] [localbranch]:[remotebranch]
git push # Push current branch to remote
git push origin # Push all matching branches to remote
git push origin serverfix # Push a local branch 'serverfix' to remote (create it if necessary)
git push -u origin serverfix # ... same but also set upstream reference for TRACKING
git push -u origin serverfix:serverfix # ... same as above
git push -u origin serverfix:coolfix # ... same but call the branch 'coolfix' on the remote
# DELETE a branch
git push origin :serverfix # Delete remote branch 'serverfix' on 'origin'
# (basically this means push nothing to remote branch 'serverfix')
git-reflog
git reflog manages reflog information. This is very handy to repair mistakes, or to recover lost commits (e.g. after modifying the head of a branch that was the only reference to a given commit)
git reflog
git-remote
Add another remote to an existing repository. Using git remote set-head
and git branch
one call also set up automatically origin/HEAD and set tracking branches. For instance the following is equivalent to what's done by git clone
:
git remote add -f origin git@griffin:repositories/my_project.git # Add remote repository and fetch automatically
git remote set-head -a origin # Set origin/HEAD automatically - see man git-remote, set-head
git branch --set-upstream master origin # Set master to track head (here origin/master) branch from origin
- git-remote set-head
- Sets / deletes the default branch for a remote. For remote origin, this creates the reference refs/remotes/origin/HEAD with content ref: refs/remotes/origin/master if default branch is master.
Use set-head to follow the changes in another branch than the default one:
git remote set-head origin -a # Set default remote branch for remote 'origin' automatically
git diff origin # -> Will show difference with origin/master (if 'master' is the default)
git remote set-head origin exoticbranch # ... or set it to a different branch (here 'exoticbranch')
git diff origin # -> Now will show diff with origin/exoticbranch
git-reset
git reset has two purposes:
- commit paths... → Index
Copy paths... from HEAD (or given commit) to index (working tree left unchanged), or - branch head → commit
Move current branch head to HEAD (or given commit), and optionally update index and/or working tree.
Modes are:
mode | index | working tree | preserve local changes |
---|---|---|---|
--soft | Yes | ||
--mixed (default) | reset | Yes | |
--hard | reset | reset | No |
--merge | reset | keep if idx = commit, else reset if idx = tree, else abort |
No (fully staged changes are lost) |
--keep | reset | keep if HEAD = commit, else reset if HEAD = tree, else abort |
Yes |
Note:
- git reset copies the old head to ORIG_HEAD.
- Caution! — Don't move the head of a branch if changes have been already published, or this will create many merge conflicts.
- Caution! — git reset --hard is a dangerous command. Prefer one of its alternatives, like:
git commit -a -m "snapshot WIP"
git reset --hard~3
|
git stash
git reset --hard HEAD~3
# ...
git reset --hard HEAD@{1} # or ORIG_HEAD
git stash apply
|
git stash
git checkout -b new-branch HEAD~3
...
git branch -D master
git branch -m new-branch master
|
Some use cases (see man git-reset
for details):
git commit ...
git reset --soft HEAD^
edit
git commit -a -c ORIG_HEAD # or -C
git commit ...
git reset --hard HEAD~3
git branch topic/wip
git reset --hard HEAD~3 # or --mixed
git checkout topic/wip # or -m topic/wip
|
git pull # conflicts
git reset --hard
git pull . topic/branch # no conflict
git reset --hard ORIG_HEAD
git pull
git reset --merge ORIG_HEAD
|
git checkout feature
#work work work
git commit -a -m "snapshot WIP"
git checkout master
#fix fix fix
git commit
git checkout feature
git reset HEAD^ # or --soft
git add foo.c
git reset -- foo.c
|
git-show
git-show shows various types of objects.
git show --stat <commit> # Show commit; for merge, show *combined* commit
git show -b -R <commit> # Show reverse patch, ignoring blanks (handy for reversing commit by hand)
Tips
Frequently Used Commands
git commit -a # Add all changes and commit in one pass
git commit --amend # Amend tip current branch (message, add some files) - also for merge commits
Working the Git Way
- Check project diff before
commit -a
: - Give
git commit
a directory argument instead of using-a
: - Clean up an ugly sequence of commits ([11]). Better than hunk-based commit because (1) each stage can be tested individually, (2) intermediate commits may contain changes that is not in the final one.
- First make sure that the ugly sequence is on some temporary branch target (what we aim for), and that end result is good and clean.
- Switch back to starting point, and do:
- Edit diff file, to select only those changes we want to include in a first commit. Then do a
git-apply diff
- Test, finalize the last changes before commits, and diff against target if necessary.
- Commit, and repeat from step 2.
- When done, branch target can be removed
- Use
gitk
to get a graphical visualisation of current commit, or some subsets. For instance - Use
git stash
to save the current state of the working tree (see [12]). - Forgot to add some files in the previous commit? Mistyped the commit message? Use
git commit --amend
:
git diff # First see what's in the working tree (or git status)
git commit -a # Commit all changes
git commit fs/ # Commit all changes in directory fs
git diff -R target > diff # diff to target
vi diff
git-apply diff # Must be in project root dir
# test test test
git diff -R target > diff # if necessary
gitk # View current commit and all ancestors
gitk master.. # View changes to current branch (i.e. reachable from HEAD, excluding master)
git stash # Save current work in working tree
... # (whatever, including git reset --hard...)
git stash apply # Bring back changes in working tree
git commit # Oups! forgot one file
git add somefile # ... Add the missing file
git commit --amend # ... and replace the previous commit
Word-by-word diffs
Reference:
- Blog [13] and [14]
- Manpage gitattributes(5)
- Manpage git-diff(1)
Add the following alias to your ~/.gitconfig file:
[alias]
wdiff = diff --color-words
wshow = show --color-words
Now you can have word-by-word diffs / shows:
git wdiff fname
git show
Improving tokenization in diffs
Diffs can even be further improved thanks to the use of the .gitattributes file in your repository. For instance:
*.tex diff=tex
*.h diff=cpp
*.c diff=cpp
*.cpp diff=cpp
One can also define filters that would clean files before checkin/checkout.
Show Git Branch in Prompt
Links:
The simplest is to add $(__git_ps1)
to variable PS1 in file ~/.bashrc. Alternatively one can use \[\e[31m\]$(__git_ps1 " (%s)")\[\e[0m\]
to prepad a space when branch name is displayed and use red color.
#PS1 in cygwin
PS1='\[\e]0;\w\a\]\n\[\e[32m\]\u@\h \[\e[33m\]\w\[\e[31m\]$(__git_ps1 " (%s)")\[\e[0m\]\n\$ '
Undoing / Reverting a commit
- See [15] (stackoverflow.com)
Undoing last commit
The much simpler git commit --amend
:
git add ...
git rm ...
git commit --amend # Current index state will be used for new commit
The usual reset / add / commit
sequence:
git commit ...
git reset --soft HEAD^ # Move back to previous commit, keep current changes
# edit edit edit...
git add ... # Add modified files
git commit -c ORIG_HEAD # Commit, reedit old comment as base (-C to reuse w/o editing)
Reverting a past commit
Use git revert ...
:
git revert -n HEAD # Revert last commit, don't commit yet
git revert -n 1234567 # Revert a given commit, don't commit yet
git commit ...
Revert with Cherry Picking changes
The easiest is to reuse the patch method as explained by Linus above in Working the Git Way:
git show -b -R <commit> > diff # Get a revert patch
vi diff # Edit the patch as necessary
git apply diff # Apply the patch
Bash Script-Fu
Some tips on how to use Git in scripts.
It is recommended to only use Git's Plumbing and Porcelain commands:
- Plumbing
- Commands not meant to be used on the command-line, but as building blocks in scripts to build more powerful command
- Porcelain
- More user-friendly commmands, but that are guaranteed not to change in the future
- Get current branch name
- The recommended way is to use
git symbolic-ref
(see [16])
branch="$(git symbolic-ref HEAD 2>/dev/null)" || branch="(no branch)"
branch=${branch##refs/heads/}
Troubleshoot
Out of memory
I get the following message under cygwin (WinXP SP3, cygwin 1.7.9-1, git 1.7.5.1-1, perl 5.10.1-5), while doing a git svn fetch
:
git svn fetch
# . . .
# . . .
# Out of memory during "large" request for 268439552 bytes, total sbrk() is 311015424 bytes at /usr/lib/perl5/vendor_perl/5.10/Git.pm line 908, <GEN1> line 615.
Solution is to increase Cygwin's maximum memory. Setting /HKLM/Software/Cygwin/heap_chunk_in_mb to 1024 (1GB) fixed the problem. Indeed Cygwin can only allocate 384MB for a process, and process ended up using more than 480MB... (see also [17])