Git

From MattWiki

Git is a free and open source distributed version control system designed to handle everything from small to very large projects with speed and efficiency.

Git Global Setup

git config --global user.name "Your Name Here"
git config --global user.email [email protected]

git config --global core.editor "vim"
git config --global color.branch auto
git config --global color.diff auto
git config --global color.interactive auto
git config --global color.status auto

Git Commands

Common Git Commands

To download a new git repository

(Public access)

git clone http://git.example.com/folder/project.git

Update an exciting repository

(From within the projects folder)

git pull

Comment a change

git comment file.txt

To Revert a comment

git revert ce963c9db00c25b3c1e6add1fe6032aef61a5bed

Commit

Modify the last commit

git commit --amend

Patches

Creating a patch

To create a patch file of the last commit

git format-patch -1 
gzip 0001-name_of_patch_file.patch

Patching a file or directory

gzip -dc 0001-name_of_patch_file.patch.gz |git apply

Branching

Creating a new branch

git branch <branch>

or to create a branch and switch to it

git checkout -b <branch>

Pulling changes from branches

git checkout <branch_committing_to>

Now that your in the branch you want to add the changes to, run:

git pull . <branch_committing_from>

Changing to a different branch

git checkout <branch>

Deleting a branch

git branch -d <branch>

Deleting a remote branch

git push origin :<branch>

Pushing a branch to github

git push <remote_repository_name> <branch_name>

So that would be...

git push origin <branch>

Display branches on github

git remote show origin

Pulling a branch from github

git checkout --track -b <name_of_local_branch> origin/<name_of_remote_branch>

Tagging

Tagging a branch

git tag -a -m "tagging version 1.0" 1.0

Pushing the tag to github

git push --tags

Deleting a tag

git tag -d 1.0

Removing a deleted tag from github

git push origin :refs/tags/1.0

Submodules

To install existing submodules

git pull
git submodule init
git submodule update

To add a new submodule to a project

git submodule add <remote-host>:<project.git> <project.git>

So if you are adding project "program" from the example git server you will run

git submodule add git://example.com/program.git program

To Update an existing submodule

cd <submodule>
git pull origin master
cd ..
git commit -a -m "Updated submodule."
git push

Import tags from SVN into an existing git repo

git gc
for c in `cat .git/packed-refs |grep remotes |grep tags |grep -v '@' |sed 's/\// /g' |awk '{print $5}'`
do
  d=`git tag |grep -x "$c" |wc -l`
  if [ "$d" == "0" ]; then
     echo "Creating new tag, $c"
     git tag -a $c -m "Converting SVN tag to GIT tag" remotes/tags/$c
     sleep 3
  fi
done

Mirroring a Repository

To Mirror a remote repository first you must check out the repository as so

git clone --mirror git://example.com/example_repo.git

After you have the repository correctly checked out, you may update it via the fetch command.

cd example_repo
git fetch

Installing Git from Source

Git Logo

First install all needed dependence:

yum install gcc perl-devel perl perl-DBD-Multi zlib-devel zlib \
openssh-server openssh libcurl expat expat-devel xinetd

Now Download Git via a tarball:

mkdir -p /var/src
cd /var/src
rm -rf git-git*
wget --no-check-certificate https://github.com/git/git/tarball/master
tar -xzf git-git*
cd git-git*

Or if you already have git installed, via git it's self:

mkdir -p /var/src
cd /var/src
git clone git://git.kernel.org/pub/scm/git/git.git
cd git

Now compile the install.

make configure && ./configure --prefix=/usr && make && make install

or

cd /var/src && rm -rf git && git clone /home/git/public/mirror/git.git && cd git && \
make configure && ./configure --prefix=/usr && make && make install && \
make GITWEB_PROJECTROOT="/home/git/secure" GITWEB_HOME_LINK_STR="secure" && \
rm -rf /var/www/code.mattrude.com/secure/gitweb.cgi && \
cp gitweb/gitweb.cgi /var/www/code.mattrude.com/secure/gitweb.cgi && echo $?

Sending emails on commits

Git's Native eMail Notifier

From the repository you wish to send email on commits.

cd .git/hooks
mv post-receive post-receive.bkp
ln -s /usr/share/git-core/contrib/hooks/post-receive-email post-receive
git config hooks.mailinglist "[email protected]"
git config hooks.emailprefix "[SUBJECT PREFIX] "  # note the trailing space

Git Commit Notifier

This 'plugin' will allow you to send an email every time a commit is committed to the repository. You may use this on the central repository to keep everyone following the project up to date.

To use this plugin, you first need to compile the script, then add the config information to each repository you wish to use it on.

To Install Git Commit Notifier from gem

On Fedora 12 you first need to install Ruby and a few other dependencies. After you download the needed dependencies, you may compile the script.

yum install ruby rubygems hpricot rubygem-hpricott
gem install git-commit-notifier

To Install Git Commit Notifier from github

yum -y install ruby rubygems rubygem-nokogiri-doc libxml2-devel libxslt-devel ruby-devel
mkdir -p /var/src
cd /var/src/
git clone git://github.com/bitboxer/git-commit-notifier.git
cd git-commit-notifier
gem build git-commit-notifier.gemspec
gem install git-commit-notifier-0.??.?.gem

To Configure Git Commit Notifier

In your repository's .git/hooks folder or if it's a "bare" repository, just the hooks folder. Create a file named "post-receive" with the following content.

#!/bin/sh
git-commit-notifier ../git-commit-notifier.yml

Once you have saved the file, you need to make it executable.

chmod 775 post-receive

After you have made the hook executable, check up one directory to the repository's .git directory. From here you need to create and modify your git-commit-notifier's config file. Start out by creating a file named "git-commit-notifier.yml", and copy the below config to it.

Git Commit Notifier Config File

# The recipient for the commit:
mailinglist: [email protected] 

# set to true if you want to ignore empty merge messages
ignore_merge: false 

# Optional parameter for the subject-line of the mail 
# emailprefix: GIT

# Decorate files with link to a webview. Possible values: none or gitweb
link_files: none 

# select the delivery method: smtp or sendmail 
delivery_method: sendmail

# settings for the smtp server
smtp_server:
  address: localhost
  port: 25
  domain: localhost
  user_name: user@localhost
  password: password
  authentication: plain
  enable_tls: false

# settings for sendmail
sendmail_options:
  location: /usr/sbin/sendmail
  arguments: -i -t

# If link_files is set to "gitweb", you need to configure the path to your gitweb
# instance and the project name.
gitweb:
  path: http://developerserver/path_to_gitweb
  project: test.git

Add all remote branches

 for b in `git remote show origin |grep tracked |awk '{print $1}'`
 do
   LOCALBRANCH=`git branch |sed 's/* //g' |sed 's/  //g' |grep $b`
   if [ "$LOCALBRANCH" != "$b" ]; then
       git branch -t $b origin/$b
   fi
 done

Creating an unattached branch with no history

From inside your git repository, after you have committed all you changes, run:

git symbolic-ref HEAD refs/heads/name-of-new-branch
rm .git/index
git clean -fdx

You will now have an empty directory waiting for your first commit.

So if your creating a new branch for github pages for example, you would run:

git symbolic-ref HEAD refs/heads/gh-pages
rm -f .git/index
git clean -fdx

And your ready to start creating your page.

Allow remote pushes to a checked out repository

In the remote repository you are planing on pushing to, run the following:

git config receive.denycurrentbranch ignore

Then download the File:Post-update.zip and unzip it. Now copy this file to your .git/hooks/ folder and make it executable.

wget http://wiki.mattrude.com/images/d/de/Post-update.zip
unzip Post-update.zip
rm -rf Post-update.zip
chmod 775 post-update
mv post-update .git/hooks/post-update

You should now be able to remotely push to this repository without errors.

Creating a new user for git secure remote pushing

adduser -b /home/git -g git -m -s /usr/bin/git-shell kelsey -p
mkdir /home/git/kelsey/.ssh
ssh-keygen -n kelsey -t dsa -f kelsey/.ssh/id_dsa
chown -R kelsey:kelsey /home/git/kelsey
chmod -R u=rwX,go-rwx /home/git/kelsey/.ssh
chmod +rw /home/git/kelsey/.ssh/id_dsa.pub

Creating a Secure Git Remote Server

  • On the Server

Create the git repo on the server

mkdir name-of-git-repo.git
cd name-of-git-repo.git
git init --bare

Create the git repo to import into the server

git init
touch README
git add README
git commit -m 'first commit'
git remote add origin git@REMOTE_SERVER:name-of-git-repo.git
git push origin master

Installing git-up

gem install git-up

Configure per user rights for a Secure Git Remote Server

I use something a bit simpler, all you need is to setup three files, the authorized_keys file, the gitsecurity.rb file and a permissions file gitpermissions. For simplicity they can all go in the git accounts .ssh folder. (Basic unix admin skills required to understand herein)

The gitpermissions file looks like this, and should be fairly self explanitory:

repo1.git|jane|rw
repo1.git|james|r
repo2.git|bob|rw

The autorized_keys file looks something like this:

command="/home/git/.ssh/gitsecurity.rb jacob",no-port-forwarding,no-X11-forwarding,no-agent-forwarding, \
no-pty ssh-rsa rFaivBw.....5Rws jacob
command="/home/git/.ssh/gitsecurity.rb bob",no-port-forwarding,no-X11-forwarding,no-agent-forwarding, \
no-pty ssh-rsa rFaivBw.....5Rws bob

Finally the gitsecurity.rb script, just copy and paste it:

#!/usr/bin/ruby

class GitPermission
    attr_accessor :username;
    attr_accessor :repository;
    attr_accessor :read;
    attr_accessor :write;

def initialize(line)
    bits = line.split('|');
    if(bits.length!=3)
        $stderr.puts "Invalid configuration file"
        Process.exit(4)
    end
    @repository = bits[0]
    @username = bits[1]
    @read = bits[2].include?("r")
    @write = bits[2].include?("w")
end

end

if(!ENV.has_key?("SSH_ORIGINAL_COMMAND"))
    $stderr.puts "SSH not allowed to the git account."
    Process.exit(1);
end
command = ENV["SSH_ORIGINAL_COMMAND"];

if(!ARGV.length == 1)
    $stderr.puts "Authorised keys file misconfigured, username not specified correctly."
    Process.exit(1);
end

if(!ARGV[0].match(/^[A-Za-z0-9]+$/))
    $stderr.puts "Authorised keys file misconfigured, username contains invalid characters: "+ARGV[0];
    Process.exit(1);
end
username = ARGV[0]

if(!command.match(/git[ -]upload-pack /) && !command.match(/^git[ -]receive-pack /))
    $stderr.puts "Only git commands are allowed."
    Process.exit(2);
end

repository = command[(command.index(' ')+1)..-1]

if(!repository.match(/'.*'/))
    $stderr.puts "Repository parameter incorrect."
    Process.exit(2);
end
repository = repository[1,repository.length-2]

begin
    file = File.new("/Users/git/.ssh/gitpermissions", "r")
    while (line = file.gets)
        p = GitPermission.new(line);
        if(p.repository == repository && p.username == username)
            if((p.write == true || (p.read == true && command.match(/^git[ -]upload-pack/))) )
                exec "/usr/local/git/bin/" + command
                Process.exit(0);
            end
        end
    end
    file.close
rescue => err
    $stderr.puts "Problem with server configuration: #{err}"
    Process.exit(4)
end

$stderr.puts "You do not have permission to complete this operation"
Process.exit(5)

Importing a SVN repo into git

To import a svn repo create a new git repo and run

git svn clone http://svn.foo.com/svn/project/ project -s

Then once your done, repack it.

git gc project/

SVN Branches

If you wish to also map your branches, you may run something like the following, note how were using awk to see only what we want to see.

for a in `cat .git/packed-refs |grep remotes |grep -v pack-refs |grep -v tags |grep -v trunk \
|grep -v '@'|awk '{print $2}'`
do
  b=`echo "$a" |sed 's/\// /g' |awk '{print $3}'`
  git branch -t $b $a
done

SVN Tags

From a checked out SVN directory, the following will give you a list of all the users in the SVN log. You will still need to updated this list before you may use it.

for a in `cat .git/packed-refs |grep remotes |grep tags |grep -v '@' |awk '{print $2}'`
do
  b=`echo "$a" |sed 's/\// /g' |awk '{print $4}'`
  echo "creating tag $b"
  git tag -a $b -m "Converting SVN tag to GIT tag" $a
  sleep 5
done

SVN Authors list

#!/usr/bin/env bash
authors=$(svn log -q | grep -e '^r' | awk 'BEGIN { FS = "|" } ; { print $2 }' | sort | uniq)
for author in ${authors}; do
  echo "${author} = ${author} <USER@DOMAIN>";
done

Once you have your list built, need to add it to your .git config. I store my authors file in the .git directory.

[svn]
    authorsfile = .git/authors

GIT/SVN Notes

Git Links

Other Git Software

References