GIT

Configure git:

git config --global user.name "Your Name Comes Here"
git config --global user.email you@yourdomain.example.com
git config --global color.diff auto
git config --global color.status auto
git config --global color.branch auto
git config --global core.autocrlf false
git config --global core.savecrlf false 
git config --global diff.renames true
git config --global merge.renameLimit 999999
git config --global diff.renameLimit 999999
git config --global core.excludesfile ~/.gitignore_global

You can now edit the global ignore file:

~/.gitignore_global
# Ignore project files from PHPStorm
.idea/*.xml
.idea/.name
.idea/scopes
.idea/*.iml

Some nice settings:

git config --global alias.st status
git config --global alias.ci commit
git config --global alias.br branch
git config --global alias.co checkout
git config --global alias.df diff
git config --global alias.undolast 'reset --soft HEAD^'
git config --global alias.datetag '!git tag `date "+%Y_%m_%d_%H%M"`'
git config --global branch.autosetuprebase always
git config --global push.default current

Checkout the first version with git:

git clone idefix.fechner.net:masterthesis.git masterthesis

Have several copies on several places and sync it with the master server (like svn):

mkdir repo
cd repo
git init repo
git pull ssh://user@server/dir/
git remote add origin git@server:pluginname.git
git config branch.master.remote 'origin'
git config branch.master.merge 'refs/heads/master'
git push origin master:refs/heads/master

Remove remote Branch

git push origin :branchname

Split one Repo into Several

We have migrated now a repo but it contains several directories which should be splitted into seperate repos. The structure looks like:

git-root/
         AutoInvite
         phpraider

This can be done with filter-branch. At first checkout the repo as often as you want to split it:

git clone git@server:auto_invite AutoInvite
git clone git@server:auto_invite AutoInvite_phpRaider

Now we remove everything from the repo which should not be in:

cd AutoInvite
git filter-branch -f --tree-filter 'rm -Rf phpraider' --prune-empty -- --all
git gc
cd ..
cd AutoInvite_phpRaider
git filter-branch -f --tree-filter 'rm -Rf AutoInvite' --prune-empty -- --all
git gc
cd ..

Now we rewrite the root directory:

cd AutoInvite
git filter-branch -f --subdirectory-filter AutoInvite -- --all
git gc
cd ..
cd AutoInvite_phpRaider
git filter-branch -f --subdirectory-filter phpraider -- --all
git gc
cd ..

Push the new repos to a gitolite server:

cd AutoInvite
git push --all git@localhost:AutoInvite
cd ..
cd AutoInvite_phpRaider
git push --all git@localhost:AutoInvite_phpRaider
cd ..

Remove everything from your local repository:

git for-each-ref --format="delete %(refname)" refs/original | git update-ref --stdin
git reflog expire --expire=now --all
git gc --prune=now

Better Tool

Maybe a better tool is: https://github.com/newren/git-filter-repo

Only keep one folder:

git-filter-repo --force --path software/telegram\ analyser

To remove the folder in the other repository:

git-filter-repo --force --path software/telegram\ analyser --invert-paths

To move the newly filtered project into ROOT path you can execute:

git-filter-repo --path-rename software/telegram\ analyser/:

Modify First Commit

The problem here is that the first commit cannot be changed with the rebase command. So we have to create a new first commit befor we can modify the real first commit:

git symbolic-ref HEAD refs/heads/newroot
git rm --cached -r .
git clean -f -d
# touch .gitignore && git add .gitignore # if necessary
git commit --allow-empty -m 'Initial commit'
git rebase --onto newroot --root master
git branch -d newroot

Now we can change commits with rebase -i as usual.

Merge Two Git Repositories

git remote add repotomerge /path/to/repo
git fetch repotomerge 
git merge repotomerge/<branch to merge>
git gc

Merge Two Git Repositories into one with own Sub Folders

We have two git repositories called subfolder1.git and subfolder2.git, both repositories will contain several branches and we want to build up a complete new structure in the way:

base directory
 |- subfolder1 (from repository subfolder1)
 |- subfolder2 (from repository subfolder2)
 |- more folder (newly added)
- file1 (newly added)

The repositories are available via git clone from:

http://server/subfolder1.git
http://server/subfolder2.git

We use the following script to checkout the repository and rewrite the path and merge everything into one repoistory with complete history and all branches:

mergeGitRepos.sh
#!/bin/bash
# (c) 2013 Matthias Fechner


me=$(basename "$0")

TMP=$(mktemp -d /tmp/$me.XXXXXXX)
echo
echo "building new repo in $TMP"
echo

set -e

cd "$TMP"
mkdir new-repo
cd new-repo
        git init
        cd ..

x=0
while [ $# -gt 0 ]
do
        repo="$1"
        shift
        dirname=$(basename "$repo" | sed -e 's/\s/-/g')
        dirname=$(basename "$dirname" | sed -e 's/.git$//')
        echo "Clone $repo"
        git clone --bare "$repo" "$dirname"

        cd "$dirname"
                echo "Checkout all branches"
                for remote in `git branch -r | grep -v master `; do git checkout --track $remote ; done
                git checkout master
                echo "Remove reference to origin, so we cannot push by accident"
                git remote rm origin
                echo "Move all files and directories to new location"
                git filter-branch --index-filter 'git read-tree --empty; git read-tree --prefix="$dirname/" "$GIT_COMMIT"' -- --all
                cd ..

        cd new-repo
                git pull --no-commit ../$dirname
                [ $x -gt 0 ] && git commit -m "merge made by $me"
                cd ..

        x=$(( x + 1 ))
done

The script is named mergeGitRepos.sh and is executed this way:

mergeGitRepos.sh http://server/subfolder1.git http://server/subfolder2

After it is done. you can copy your missing folder (more folders) and your missing files and commit them as usal.

Second Approach

The script failed and do not what I wanted. New approach is to filter every repository to get the correct path there.

At first we copy the repository on the server so we work on a test repository, that is important, because we will overwrite it several times to get everything merged.

cp -r repo.git repo_save.git

Now the real work:

git clone http://server/repo_save.git
cd repo_save
for remote in `git branch -r | grep -v master `; do git checkout --track $remote ; done
git checkout master
git remote rm origin
git filter-branch --index-filter 'git read-tree --empty; git read-tree --prefix="subfolder/" "$GIT_COMMIT"' -- --all
git push -f --all http://server/repo_save.git
git push --tags http://server/repo_save.git
cd ..
rm -rf repo_save

Redo this for every repository. Now all repositories should have there correct folder displayed for all branches.

Now we combine them to one repository:

mkdir new-repo
cd new-repo
git init
git remote add origin http://server/repo1_save.git
git fetch --all
git fetch --all -t
git pull
git checkout master
for remote in `git branch -r | grep -v master `; do git checkout --track $remote ; done
git checkout master
git remote rm origin


git remote add origin http://server/repo2_save.git
git fetch --all
git fetch --all -t
git merge origin/master
... merge all branches ....
git remote rm origin

git remote add origin http://server/new-repo.git
git push --all
git push --tags
cd ..
rm -rf new-repo

Now you have your new repository on the server in new-repo.git.

Clone it, test it thate everything like you expected it. If everything is fine, remove all backup and you are done.

Delete files from git history

You have a project and you want to make it public but you commited password to your repository? Lets say the passwords are in the file bin/config, so we can do the following:

git filter-branch --index-filter 'git rm -r --cached --ignore-unmatch bin/config' HEAD

Altium

use the following ignore definition:

__Previews/
History/
* Logs/*
Project Output *
*.DSNlck

Changelog

To create a changelog:

git log change_20151110..change_20160114  --pretty=format:'- %s' --reverse |grep -v Merge

To create a html output:

git log change_20151110..change_20160114  --pretty=format:'<li> <a href="http://server/?p=repository;a=commit;h=%H">view commit &bull;</a> %s</li> ' --reverse |grep -v Merge

Git on Windows

To install git for windows go to website: https://git-for-windows.github.io/ and download the latest version. I tested it with Git-2.3.5.8-dev-preview-32-bit.exe.

Apache virtual host configuration:

gitweb.conf
<VirtualHost *:80>
SetEnv GIT_PROJECT_ROOT "D:/Gitrepos"
SetEnv GIT_HTTP_EXPORT_ALL
ScriptAliasMatch \
	"(?x)^/(.*/(HEAD | \
			info/refs | \
			objects/(info/[^/]+ | \
				[0-9a-f]{2}/[0-9a-f]{38} | \
				pack/pack-[0-9a-f]{40}\.(pack|idx)) | \
			git-(upload|receive)-pack))$" \
			"D:/Git/mingw64/libexec/git-core/git-http-backend.exe/$1"

DocumentRoot "D:/Git/mingw64/share/gitweb"
<Directory "D:/Git/mingw64/share/gitweb">
	DirectoryIndex gitweb.cgi
	<Files *.cgi>
		SetHandler cgi-script
	</Files>
	Options ExecCGI FollowSymLinks
	SetEnv  GITWEB_CONFIG "d:/Gitrepos/gitweb.conf"
	AuthType Digest
	AuthName "Git Password Required"
	AuthDigestDomain /git/
	AuthUserFile "D:/Gitrepos/htdigest_passwd"
	Require valid-user
</Directory>
<Directory />
	AuthType Digest
	AuthName "Git Password Required"
	AuthDigestDomain /git/
	AuthUserFile "D:/Gitrepos/htdigest_passwd"
	Require valid-user
</Directory>
</VirtualHost>

Edit the file d:\Git\mingw64\share\gitweb\gitweb.cgi and change the first line to:

#!D:/Strawberry/perl/bin/perl.exe

And modify the function get_file_owner:

sub get_file_owner {
	my $path = shift;

	my $owner = "Matthias Fechner";
	return to_utf8($owner);
}

Create a config file for gitweb:

d:\gitrepos\gitweb.conf
our $GIT = "d:/Git/bin/git";
our $projectroot= "d:/Gitrepos";
our $git_temp = "/d";
our $projects_list = "d:/Gitrepos/projects.list";
our $export_ok = "git-daemon-export-ok";
our $strict_export = "false";

If you get an error message that CGI.pm is missing you can install it manually using the cpan shell. At first we define a possibly required proxy, so open a git shell and do:

export http_proxy=http://proxyhost:proxyport/
export ftp_proxy=http://proxyhost:proxyport/
cpan install CGI

If you have problems with proxy authentication you can configure user and password also manually with:

cpan
o conf http_proxy proxyhost
o conf ftp_proxy proxyhost
o conf proxy_user username
o conf proxy_passw password
o conf commit
exit

Automatic Deploy

We have an application running on tomcat and the application is managed by a git repository. As application server we use tomcat on a windows server having git installed. Now I would like to have a simple script I fire that stops the appliation server, pull changes, checkout it, remove all files not included in commits, rollout configuration files and restart tomcat.

Configuration files are stored with file extension .STG, .STG1, .PRD, .PRD1, .PRD2, .PRD3… and the normal configuration file is not part of git repository. So we can have different configuration files in the git repository and the script will take the correct one and copy it accordingly.

We do this using a simple shell script. For this create a link that has as target the following link defined:

C:\WINDOWS\system32\cmd.exe /c ""C:\Program Files\Git\usr\bin\sh.exe" --login -i updateapp.sh"

Now go into your home directory and create a file called updateapp.sh:

updateapp.sh
system="STG"
node="STG2"

net stop Tomcat7
echo Clear cache files
rm -rf /d/Archibus/apps/webcentral/tools/tomcat/webapps/archibus/schemaCompiled
rm -rf /d/Archibus/apps/webcentral/tools/tomcat/work/Catalina

echo
echo Update archibus folder
cd /d/Archibus/apps/webcentral/tools/tomcat/webapps/archibus
git checkout -f master
git fetch
git reset --hard origin/master
git clean -fd

echo
echo Update configuration files
for i in $(find . -type f -name "*.$system");
do
  cp -v "$i" "${i%.$system}"
done

for i in $(find . -type f -name "*.$node");
do
  cp -v "$i" "${i%.$node}"
done

echo
echo Update archibus_help folder
cd /d/Archibus/apps/webcentral/tools/tomcat/webapps/archibus_help
git checkout -f master
git fetch
git reset --hard origin/master
git clean -fd

echo Press a key to start Tomcat
read
net start Tomcat7
echo
echo Update finished
echo Press any key to exit
read

GitoLite

Install gitolite

Gittolite is a nice tool to manage access and git repositories with an additional admin-repository.

Server Site

Install the port by:

cd /usr/ports/devel/gitolite/
make install
make clean

Now we create the home dir of the git user:

mkdir /usr/local/git
chown git:git /usr/local/git

Set a password for the user git:

passwd git

Client Site

Now we copy our public key to the server with:

scp ~/.ssh/id_rsa.pub git@server:name.key

Now login with the user git and ssh:

ssh -o PubkeyAuthentication=no git@server

Now we setup the gitolite repo:

gitolite setup name.key
exit vi without modification on the file.
exit

Checkout the admin repository with:

git clone git@server:gitolite-admin

Do your modification, commit it and push it back to the server.

Move existing repo to gitolite

At first we create a new repo using the gitolite mechanism by editing the file gitolite.conf from the cloned gitolite-admin repository:

repo newrepo
  RW+ = user

Commit this change and push it to the server.

Now we can push our existing repo into gitolite. Go to the repo you want to push into your gitolite managed repos and insert:

git push --all git@server:newrepo

Upgrade Gitolite v3

Upgrade gitolite using normal FreeBSD upgrade (portmaster or portupgrade). After this is finished we have to upgrade the hook scripts.

Login into the server with the git account:

ssh -o PubkeyAuthentication=no git@server

Upgrade with:

gitolite setup

That’s it.

EMail Notifier

gem install git-commit-notifier

Convert SVN to GIT

First step is to create a translation file for the users. Follow here [[git:git_and_svn?s[]=checkout#checkout_a_svn_repo]] to create this author file.

Download the script [https://raw.githubusercontent.com/schwern/svn2git/master/svn2git].

Then we will clone the repository:

export repo=arinc653

git svn clone file:///usr/local/svn/${repo} -A authors.txt -s ${repo}
cd ${repo}
git branch -a -v

# you only have to execute this if you have branches or tags in the repo
svn2git --no-clone

git remote add origin git@localhost:${repo}
git gc --aggressive
git fsck --unreachable
git push --all
cd ..

rm -Rf ${repo}
rm -Rf /usr/local/svn/${repo}

Convert CVS to Git

Install cvs2svn with git support:

cd /usr/ports/devel/cvs2svn
make install
make clean
mkdir migrate-old-repos-to-git
cd !$
cp /usr/local/share/examples/cvs2svn/cvs2git-example.options cvs2git.options

Edit the file like this:

 diff -ud cvs2git.options.old cvs2git.options                                                  idefix@server
--- cvs2git.options.old 2014-06-11 13:00:54.939373946 +0200
+++ cvs2git.options     2014-06-11 13:04:39.698356333 +0200
@@ -188,7 +188,7 @@ ctx.trunk_only = False
 ctx.cvs_author_decoder = CVSTextDecoder(
     [
         #'utf8',
-        #'latin1',
+        'latin1',
         'ascii',
         ],
     #fallback_encoding='ascii'
@@ -196,7 +196,7 @@ ctx.cvs_author_decoder = CVSTextDecoder(
 ctx.cvs_log_decoder = CVSTextDecoder(
     [
         #'utf8',
-        #'latin1',
+        'latin1',
         'ascii',
         ],
     #fallback_encoding='ascii',
@@ -207,7 +207,7 @@ ctx.cvs_log_decoder = CVSTextDecoder(
 ctx.cvs_filename_decoder = CVSTextDecoder(
     [
         #'utf8',
-        #'latin1',
+        'latin1',
         'ascii',
         ],
     #fallback_encoding='ascii'
@@ -513,7 +513,7 @@ ctx.retain_conflicting_attic_files = Fal
 # (name, email).  Please substitute your own project's usernames here
 # to use with the author_transforms option of GitOutputOption below.
 author_transforms={
-    'jrandom' : ('J. Random', 'jrandom@example.com'),
-    'mhagger' : 'Michael Haggerty <mhagger@alum.mit.edu>',
-    'brane' : (u'Branko Čibej', 'brane@xbc.nu'),
-    'ringstrom' : 'Tobias Ringström <tobias@ringstrom.mine.nu>',
-    'dionisos' : (u'Erik Hülsmann', 'e.huelsmann@gmx.net'),
+    'idefix' : ('Matthias Fechner', 'spam@fechner.net'),
@@ -561,7 +561,7 @@ run_options.set_project(
     # The filesystem path to the part of the CVS repository (*not* a
     # CVS working copy) that should be converted.  This may be a
     # subdirectory (i.e., a module) within a larger CVS repository.
-    r'test-data/main-cvsrepos',
+    r'/usr/local/cvs/bericht_pra2',

     # A list of symbol transformations that can be used to rename
     # symbols in this project.
export repo=bericht_pra2

mkdir cvs2git-tmp
cvs2git --options=cvs2git.options
mkdir ${repo}.git
cd ${repo}.git
git init --bare
git fast-import --export-marks=../cvs2git-tmp/git-marks.dat < ../cvs2git-tmp/git-blob.dat
git fast-import --import-marks=../cvs2git-tmp/git-marks.dat < ../cvs2git-tmp/git-dump.dat
git gc
git remote add origin git@localhost:${repo}
git push origin
cd ..

rm -Rf ${repo}.git cvs2git-tmp
rm -Rf /usr/local/cvs/${repo}

GIT and SVN

Checkout a SVN Repo

At first we have to create a translation file for the authors:

svnuser1 = First User <user@hellospambot.com>
svnuser2 = Another User <anotheruser@whatever.com>

To get a list of users you can execute the following small shell script:

#!/bin/sh
#
# Extract information from /etc/passwd and build up a translation file for git.
# It extracts the infos to a file called authors-PID.txt
#
# (c) 2010 Matthias Fechner
#
TMPFILE=/tmp/tmp-authors-$$.tmp
EXPORTFILE=authors-$$.txt
svn -q log | grep '^r' | cut -d ' ' -f 3 | sort | uniq > $TMPFILE
rm $EXPORTFILE
for i in `cat $TMPFILE`
do
echo "Search user $i"
echo -n "$i = " >> $EXPORTFILE
USERSTRING=`grep '^'$i':' /etc/passwd | cut -d ':' -f 5`
echo "  found $USERSTRING"
FULLNAME=`echo $USERSTRING | cut -d ',' -f 1`
echo "  found $FULLNAME"
echo -n $FULLNAME >> $EXPORTFILE
EMAIL=`echo $USERSTRING | cut -d ',' -f 5`
echo " <$EMAIL>" >> $EXPORTFILE
done
rm $TMPFILE

To get only a list of all people committed into the repository:

svn -q log | grep ^r | cut -d '|' -f 2 | sort | uniq

Then we can do a clone:

git svn clone <svn repo url> -A authors.txt -s <destination dir name>

Working on it

With git-svn, you get by default a local branch named master. You should not do any work on it, only keep it up-to-date with the svn trunk branch.

git checkout master
git svn fetch
git svn rebase

If you want to do some modifications create a local branch:

git branch local-devel
git checkout local-devel

Now change the code, test it and do local commits:

fix the bug, compile, test,
git commit -a
fix the bug, compile, test,
git commit -a

If you ready and want to commit it to the remote repository we have to update our local trunk and rebase (do not use git merge, it will through away your commit messages) it:

git checkout master
git svn fetch
git svn rebase
git rebase --interactive --preserve-merges local-devel
git svn dcommit

Now we can remove our local branches:

git branch -D local-devel

Convert subversion to git with not all branches and tags

Init the new repository:

mkdir newdir
cd newdir
git svn init svn://server/path -s
git config svn.authorsfile ~/authors

Now edit .git/config

[svn-remote "svn"]
    url = svn://server
    fetch = server/trunk:refs/remotes/trunk
    branches = server/branches/{branch1, branch2}:refs/remotes/branches/*

Then clone it with:

git svn fetch

Ignore all files which are ignored by subversion:

git svn show-ignore > .gitignore

Convert all remote branches and tags to local ones by using the script svn2git :

svn2git --no-clone

Cleanup:

git gc --aggressive
git fsck --unreachable

Using GIT as Central Repository on a Server

To use a git repository on a central server to sync it between several computers we will convert it to a bare repository and place the bare repository on a place we like to share it. For this the first step is to create this bare repository with:

umask 007
cd /usr/local/gitroot
git clone --bare /tmp/repo-to-clone.git my-shared-repo.git

Finally we have to set some parameters to share it:

cd my-share-repo.git
git config core.sharedRepository 1
git config receive.denyNonFastForwards true
find objects -type d -exec chmod 02770 {} \;

Check ‘git help config’ to see what the parameters will do. If you create a new repository you can use also git init --shared my-share-repo.git.

Now can can clone the repository change files commit it and push it to the server.