How to use git subtree split with tags?

Git is a fantastic VCS and I use it at home, at work, every days.

I have a library (BeSimpleSoap) that contains parts of Server, Client, Common, Wsdl and the Bundle for Symfony2. But if you have a project who needs just the SoapClient part, you are forced to download other parts of the library (with vendors if you use Composer).

Fortunately with Git there is always a solution! From Git v1.7.11 a new command is available and it's git-subtree.

The documentation of this command says:

NAME
----
git-subtree - Merge subtrees together and split repository into subtrees

Split all libraries into other repositories with all branches#

I use it mainly to split the BeSimpleSoap repository:

#!/bin/bash

# BeSimpleSoapSplitter.sh

git fetch origin

# find branches named master or `x.x` on origin
BRANCHES=( `git branch --remotes | grep '^ *origin/\([0-9]\.[0-9]\|master\)'` )

# iterate on branchesx
for branch in ${BRANCHES[@]} ; do
  # remove "origin/" on branch name
  branch=`echo $branch | sed -e 's/origin\/\([0-9]\.[0-9]\|master\)/\1/'`

  # create/reset branch based on remote
  git checkout -B subtree_$branch origin/$branch &>/dev/nul

  # iterate on libraries
  for repoName in `ls -1 src/BeSimple` ; do
    # use subtree to split library into new branch
    git subtree split --prefix=src/BeSimpleSoap/$repoName -b subtree_${repoName}_$branch

    # add remote identified by $repoName
    git remote add $repoName git@github.com:BeSimple/BeSimple$repoName.git &>/dev/null

    # push local branch on remote
    git push $repoName subtree_${repoName}_$branch:$branch
  done
done

Okay good. With script I can split all libraries into other repositories with all branches (master and x.x branches).

But tags are not added on subtrees.

Compute tags on demand#

In a Git repository, a tag name can to exist just once.

In my case I create a new branch from a specified tag and I split libraries. After I deduce that last commit of each subtree is the position of tag.

#!/bin/bash

# BeSimpleSoapSplitter.sh [tag]
# eg: BeSimpleSoapSplitter.sh v0.2.0

function splitRepository() {
  # iterate on libraries
  for repoName in `ls -1 src/BeSimple` ; do
    # use subtree to split library into new branch
    git subtree split --prefix=src/BeSimpleSoap/$repoName -b subtree_${repoName}_$1

    # add remote identified by $repoName
    git remote add $repoName git@github.com:BeSimple/BeSimple$repoName.git &>/dev/null

    if [ -z "$2" ] ; then
      # push local branch on remote
      git push $repoName subtree_${repoName}_$branch:$branch
    else
      # create tag on last commit of subtree
      git tag $2 subtree_${repoName}_$1

      # push new tag on remote
      git push $repoName --tags

      # delete tag to create on other subtree
      git tag -d $2
    fi
  done
}

git fetch origin

# test if a tag has been specified
if [ -n "$1" ] ; then
  TAG=$1 ; fi

  # test if tag exist
  git tag -l | grep $TAG &>/dev/null
  if [ 1 -eq "$?" ] ; then
    echo "TAG \"$TAG\" does not exist"

    exit 1
  fi

  # create/reset branch based on tag
  git checkout -B subtree_tag_$TAG $TAG &>/dev/null

  # delete tag because it's not possible to have 2 tags with same name
  git tag -d $TAG

  # call splitRepository function with branch name and the name of tag
  splitRepository tag_$TAG $TAG

  # fetch origin to recreate the tag
  git fetch origin

  exit 0
fi

# find branches named master or `x.x` on origin
BRANCHES=( `git branch --remotes | grep '^ *origin/\([0-9]\.[0-9]\|master\)'` )

# iterate on branches
for branch in ${BRANCHES[@]} ; do
  # remove "origin/" on branch name
  branch=`echo $branch | sed -e 's/origin\/\([0-9]\.[0-9]\|master\)/\1/'`

  # create/reset branch based on remote
  git checkout -B subtree_$branch origin/$branch &>/dev/nul

  # call splitRepository function with branch name
  splitRepository $branch
done

Enjoy!