GitTips
OBSOLETE CONTENT
This wiki has been archived and the content is no longer updated. Please visit git-scm.com/doc for up-to-date documentation.
See also:
- GitFaq (Frequently Asked Questions) page.
- GitWorkflows which attempts to describe actual, useful, real-world things that people do with git, step by step.
- ExampleScripts - You can do a lot by writing a simple script.
- Aliases are very powerful ways to avoid writing a script.
- git ready - daily tips for the noob to the guru
Table of contents:
General
How to fix the most recent commit
Git allows you to easily fix up the most recent commit you've made on a branch with the --amend option:
For example the following command will allow you to alter the commit message at the top of current head:
$ git commit --amend
while
$ git commit -s --amend
will let you alter the commit message and will also automatically add a sign-off message for you.
How to change commits deeper in history
Since history in Git is immutable, fixing anything but the most recent commit (commit which is not branch head) requires that the history is rewritten from the changed commit and forward.
You can use StGIT for that, initialize branch if necessary, uncommitting up to the commit you want to change, pop to it if necessary, make a change then refresh patch (with -e
option if you want to correct commit message), then push everything and stg commit.
Or you can use rebase to do that. Create new temporary branch, rewind it to the commit you want to change using git reset --hard
, change that commit (it would be top of current head), then rebase branch on top of changed commit, using git rebase --onto <tmp branch> <commit after changed> <branch>
.
Or you can use git rebase --interactive
, which allows various modifications like patch re-ordering, collapsing, ...
git filter-branch
can also be useful. It has been ported from the obsoleted Cogito's cg-admin-rewritehist(1).
How to get only merges in gitk?
With recent version of git you can use
gitk --full-history -- a//b
which will give you each commit that changes that nonexistent file (because there can't be file with double slash in name in git repository), and the full commit history for those (i.e. all the merges).
If you use "git log", you also need to add "--parents" while gitk will do it for you.
What to do if you have realized that you are on wrong branch?
If you have not committed your changes yet, it is enough to
git checkout -m <correct-branch>
(if you use core Git at least v1.4.1-rc2).
(Cogito users will do cg switch <correct-branch>
)
Let's assume that you wanted your changes to go on top of <current>, but by accident you committed your changes on top of <master> for example, and that the commit before your commits (the tip of <master> branch should be) is <before>. Let as assume that all changes are committed, and the working dir is clean.
First, create temporary branch (just in case), e.g.
git branch <tmpBranch>
Then you can reset <master> branch to correct commit
git reset <before>
Move temporary branch to correct branch, i.e. <current>, using
git rebase --onto <current> <master> <tmpBranch>
After resolving all conflict what is left is to fast-forward <current> to correct commit, and remove temporary branch
git checkout <current> git reset --hard <tmpBranch> git branch -d <tmpBranch>
I'm not sure if --hard
option is really needed.
How to compare two local repositories
In Git you can use, being in one of repositories
GIT_ALTERNATE_OBJECT_DIRECTORIES=../repo/.git/objects git-diff-tree $(GIT_DIR=../repo/.git git rev-parse --verify HEAD) HEAD
if you want to compare current repository with ../repo/
repository.
In the obsoleted Cogito you would nearly exactly the same
GIT_ALTERNATE_OBJECT_DIRECTORIES=../repo/.git/objects cg-diff -r `GIT_DIR=../repo/.git cg-object-id -c HEAD`..HEAD
How to use git to track OpenDocument (OpenOffice, Koffice) files?
Using clean and smudge filters to diff and merge on the XML
http://kerneltrap.org/mailarchive/git/2008/9/15/3305014 suggests a method which stores the unzipped OpenDocument file in git. Thus you should be able to both diff and merge the plain XML of two versions. However, note this warning about the "rezip" approach.
Getting a plain-text diff
The alternative is to store the binary, and have an alias to convert to plain text for diffing.
Instructions for Git 1.6.1 or later
Git now comes with the textconv features, which allows using an arbitrary command to convert a file to text before diffing. It makes it very easy to set up, and allows keeping all the goodness of git diff like --color, --color-words, ... To have more information on textconv features, and to see some demos, please refer to this article
First, install odt2txt, a simple (and stupid) converter from OpenDocument to plain text, and configure git to allow it to run it, by adding this to ~/.gitconfig:
[diff "odf"] textconv=odt2txt
Now, for each project, you just need to ask git to use this driver in .gitattributes or $GIT_DIR/info/attributes, like this:
*.ods diff=odf *.odt diff=odf *.odp diff=odf
... and you're done! Enjoy "git diff", "git log -p", "git show" in this project.
Instructions for older Git's
To view plaintext diff of OpenDocument files in Git, you can use the GIT_EXTERNAL_DIFF environment variable. First, install :
- odt2txt: A simple (and stupid) converter from OpenDocument to plain text
- git-oodiff: Wrapper script for odt2txt and diff.
You can now, for example, do
$ GIT_EXTERNAL_DIFF=git-oodiff git diff git-oodiff presentation-expl.odp presentation-expl.odp --- a/presentation-expl.odp +++ b/presentation-expl.odp @@ -3,7 +3,7 @@ First item - First version of second item + Second version of second item Last item
Now, let's automate this a bit more. Add this to your ~/.gitconfig (or .git/config):
[diff "oodiff"] command=git-oodiff
This defines a "oodiff" diff driver, that you can now use in .gitattributes (at the root of your working tree, or in $GIT_DIR/info/attributes)
*.odp diff=oodiff *.odt diff=oodiff *.ods diff=oodiff
Now, git will use this oodiff driver for any file whose name ends with .odp, .odt, or .ods.
(this is also documented here).
user-note:
- BUG in git-oodiff script - it will give an error if /bin/sh points to "dash" instead of "bash" (this will affect current debian & ubuntu distros)
How to remove all files which are missing from working directory?
git ls-files -z --deleted | git update-index -z --remove --stdin
To both remove files that are missing, and add all files that are new, one can use the (much easier) command:
git add -A
How to ignore files which are "Untracked" now?
$ git ls-files -o --exclude-standard >> .gitignore $ $EDITOR .gitignore
(note : --exclude-standard
is not yet in a released version of git as of november 2007, you'll have to use --exclude-from=.gitignore --exclude-from=.git/info/exclude ...
if you don't have it).
How to unpack all branches?
For example if you have realized that you repository must be accessible by older dumb clients, or some script you use doesn't understand packed refs.
git for-each-ref refs/heads | \ ( x=`git rev-parse --git-dir`; \ while read id junk ref; do \ echo $id > "$x/$ref"; \ done )
How to find a commit for a shortened git-describe string
For your local (!) repository you can get the commit for a string like v2.6.26-rc9-56 by the following command :
git rev-list --all v2.6.26-rc9.. | xargs git describe | grep '\-56\-'
How to create a new branch that has no ancestor
Why? You might want to create a new component in a branch by itself, or perhaps you want to create a test case branch, or perhaps a documentation branch, any of which you want to keep separate from other development branches because they have completely different files.
So, here is how to add a new and empty branch.
The easy way: Use git checkout
's --orphan
flag, which has been available since v1.7.2:
git checkout --orphan new-branch optional-starting-point git rm -rf . # start afresh ... # add stuff to the index. git commit # make the root commit
The first line creates a new branch named new-branch
, and the index and the working tree are set as if you had previously run git checkout optional-starting-point
(or git checkout HEAD
if no optional-starting-point
is given). The first commit made on new-branch
becomes a root commit, having no parents and thus being completely disconnected from existing history.
Since the index and working tree are already prepared with data, just running git commit
creates a root commit with the same file contents as that provided by optional-starting-point
. However, a completely different set of data can be committed by first clearing the index and the working tree; this is most safely achieved by running git rm -rf .
from the top level of the working tree. Then the root commit may be prepared as usual.
Alternatives
- Create a new repository, start the branch, and push this branch into your original repository. Example:
mkdir anew cd anew git init # add some files here cp ../<files...> ./ git add . git commit git push .. HEAD:new-branch-without-ancestor cd .. rm -rf anew
- Reuse the working directory, but initialize a new repository with a new branch, and then fetch the branch. Example:
git --git-dir=tmp.git init # add some files git --git-dir=tmp.git add <files...> git --git-dir=tmp.git commit git fetch tmp.git master:new-branch-without-master rm -rf tmp.git
- You can make life hard on yourself instead:
git symbolic-ref HEAD refs/heads/mynewbranch rm .git/index git clean -fdx # add some files here git add . git commit
How does this work?
git symbolic-ref HEAD refs/heads/mynewbranch
HEAD is a symbolic reference that refers to the current branch. Try doing 'git symbolic-ref HEAD' and it will show you the actual reference to which it refers. By providing that second argument, you are saying that you now want the symbolic reference HEAD to refer to this new reference refs/heads/mynewbranch. Voila! a new branch is created. <p/> But the branch is not yet empty ... which bring us to the next couple commands.
rm .git/index git clean -fdx
The first command removes the index file with with Git is tracking the working directory changes. No index, no tracking.
<p/>
The second command will clean out all untracked files (which is now everything except for the Git repository files. This is a safe way of emptying the directory (whereas rm -rf * is NOT safe :-). Alternatively, you may use the very safe git rm -rf .
command.
<p/>
Now you can create one or more files that will be part of the initial commit of this new branch. Then add and commit, as you normally would. For example:
git add . git commit
That's it. A bit tricky at the start, but not too tricky.
References
- http://book.git-scm.com/5_creating_new_empty_branches.html
- http://madduck.net/blog/2007.07.11:creating-a-git-branch-without-ancestry/
Configuration
Specify attribute values in the correct format
When specifying values for gitattributes(5) do not include white space around '=' as whitespace is the attribute delimiter. This differs from git configuration files where white space around '=' is supported.
Turn off translation (locale) for Git
To make Git be not localized, to avoid using translation (e.g. because it is confusing, and there is no translated documentation) you can use the following hack. Set GIT_TEXTDOMAINDIR to something nonsensical (like "/"), which will mean git cannot find the *.po files, and just uses the builtin messages.
How to upgrade repository config to use post-1.5.0 features?
Updating a pre-1.5.0 Git repository to be like a 1.5.0 (and later) style repository basically means rewriting the remotes (.git/info/remotes/) into .git/config, setting up wildcard tracking branches under refs/remotes and deleting the old tracking branches from the refs/heads namespace.
The following shell script can be used to upgrade non-bare (!) repository (with a working directory) to post-1.5.0 style, to be able to use new features:
#!/bin/sh ## ## Upgrades a pre-1.5.0 repository to a 1.5.0-style layout. ## Use at your own risk. ## sh remotes2config.sh # from git.git/contrib git config core.bare false git config core.logallrefupdates true for r in `git remote` do old="`git config --get-all remote.$r.fetch | sed s,^.*:refs/heads/,,`" if [ -z "$old" ] then echo "No fetch lines for $r." continue fi echo "Converting $r..." git config --replace-all remote.$r.fetch +refs/heads/*:refs/remotes/$r/* git fetch $r for r in $old do case "$r" in *:*) echo "Not deleting $r" ;; *) git branch -D $r ;; esac done done
This script requires remotes2config.sh conversion tool by Johannes Schindelin, which can be found in the contrib area of git.git repository.
- Upgrade to 1.5.0 utility post by Shawn O. Pearce on Git mailing list
StGIT
How to rebase StGIT stack?
If you have <n> patches on stack, and you want to rebase it (for example because git branch got rebased, or you want to base stack on different branch)
stg pop -a git reset --hard <new_base> stg push -n <n>
Web
How to generate RSS feed off-line?
Perl script by Bennett Todd you can either adapt to your needs (which is mentioned on Interfaces Frontends And Tools page)
May be used as a script. Assuming that gitweb.cgi
is setup correctly, you can use:
env REQUEST_METHOD=GET QUERY_STRING='p=git/git.git;a=rss' \ ./gitweb.cgi | tail -n +2
where of course instead of git/git.git
you put project (git repository) you are interested in.
Ruby Gem which does Git to RSS conversion and includes a standalone web server for the RSS if desiered.
How to push/pull via ssh to host behind gateway?
There are two possible solutions. With first, using ProxyCommand option of ssh, you can connect to the entire network behind firewall. In your ssh configuration file, for example
~/.ssh/config
put the following:
Host *.foo.internal ProxyCommand ssh gateway.foo.com exec nc %h %p
(where '%h'
expands to the host you connect to, and '%p'
to the port you are using). You have to have nc
(netcat or GNU Netcat) installed on gateway, but this should be no problem as it is 22K binary. Note that path is expanded on local side, while it should be to file on gateway side.
Second is slightly faster, doesn't need any tool besides ssh, but you have to run port forwarding command before connecting, must make sure that ssh tunnel is up, and must be set up host for host. In your ssh configuration file, for example ~/.ssh/config
put the following:
Host hostname NoHostAuthenticationForLocalhost yes HostName localhost Port 2222
where `hostname' is the name you would be using for connecting. Before trying to connect to host begind gateway, you must run
ssh -f -N -L 2222:host.foo.internal:22 username@gateway.foo.com
Of course port numbers must match (and port number should be above 1024 if you won't be running ssh tunnel as root). If you have sufficiently new ssh, you can add '-M'
option, for use with ControlMaster option. You can also use autossh for running port forwarding/ssh tunnel.
How to pass ssh options in git?
You can set the path of the ssh executable to use with the GIT_SSH environment variable. Create a shell script like
#!/bin/sh exec ssh --your-options-- "$@"
and make GIT_SSH point to it.
But if you have just a couple of "standard" setups, then it's really better to use a ".ssh/config" file instead. If you want to use different options "dynamically" (the same host, different options), you can just use different "fake hostnames". For example, you can do
Host private.host.com User myname Hostname host.com IdentityFile /home/myname/.ssh/private-identity Host public.host.com User groupname Hostname host.com IdentityFile /home/myname/.ssh/public-identity
and now you can ssh to "host.com" using different identities by just using "private.host.com" and "public.host.com" respectively.
Using gmail to send your patches
git send-email now supports TSL/SSL, so using gmail is as simple as setting the following configuration variables:
[sendemail] smtpencryption = tls smtpserver = smtp.gmail.com smtpuser = yourname@gmail.com smtpserverport = 587
It'll ask you for your password when you're sending the emails. Like so:
git format-patch --no-color -C -M origin/master..topic-branch -o outgoing/ git send-email --compose outgoing/00*
Using msmtp to send your patches
NOTE: git send-email supports TLS/SSL now. If you are looking for gmail tips see the section above.
Mailing off a set of patches to a mailing list can be quite neatly done by git-send-email. One of the problems you may encounter there is figuring out which machine is going to send your mail. I tried smtp.gmail.com, but that one requires tls and a password, and git-send-email could not handle that.
A neat little program, msmtp http://msmtp.sourceforge.net/ can help you there. Install and configure a .msmtprc file in your home directory, the likes of:
# Example for a user configuration file # Set default values for all following accounts. defaults tls on tls_trust_file /etc/pki/tls/certs/ca-bundle.crt logfile ~/.msmtp.log # My email service account gmail host smtp.gmail.com port 587 from some.user.name@gmail.com auth on user some.user.name@gmail.com password my-secret # Set a default account account default : gmail
This takes gmail as an example. The password field is not required. msmtp will prompt you for your password if the field is omitted. The ca-bundle.crt is the file with CA certificates for Fedora Core 6, for other distros you might have to dig it up from somewhere else. On Ubuntu /etc/ssl/certs/ca-certificates.crt should be used, for example. Now, by giving the full path name to the msmtp program as smtp server to git-send-email, you can send the patches through gmail or some other smtp account with TLS and/or user authentication.
git send-email --smtp-server /usr/local/bin/msmtp <file|directory>
An alternative to setting --smtp-server each time is to set the global sendemail.smtpserver value.
git config --global sendemail.smtpserver /usr/local/bin/msmtp
Another option is to configure a local smtp server on your machine, using a well-known SMTP server as smarthost. Then, all applications using localhost as a mail server (e.g. /usr/bin/mail) will work.
On OS X with macports, msmtp can be installed with 'sudo port install msmtp', and '/usr/share/curl/curl-ca-bundle.crt' should work for tls_trust_file.