GIT

May 24, 2020
6 min read
May 27, 2023 09:13 EEST

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

Related Posts