Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
# This script "unsubmodulizes" a git repository - i.e. it takes a git
# repository with submodules and replaces the submodules with the
# history of the submodule merged into a subdirectory of the same
# name. I knocked this up quickly as an answer to this stackoverflow
# question:
# ... and so it hasn't been tested much: use entirely at your own
# risk, etc. Afterwards, your git repository will have multiple root
# commits.
import datetime
import re
import sys
from subprocess import Popen, PIPE, check_call, call
# Exit unless the working tree matches the index and the index matches
if 0 != call(["git","diff","--exit-code"]):
print >> sys.stderr, "There are unstaged changes - git status should be clean"
if 0 != call(["git","diff","--cached","--exit-code"]):
print >> sys.stderr, "There are changes staged but not committed - git status should be clean"
# Find the names of every submodule and the commit that the submodule
# should be at:
submodules = []
p = Popen(["git","ls-files","--error-unmatch","--stage"],stdout=PIPE)
output = p.communicate()[0]
if p.returncode != 0:
raise Exception, "Finding the submodules failed"
for line in output.decode().splitlines(False):
m ='^160000 ([a-f0-9]{40}) \d+\s+(.*)$',line)
if m:
c, s = m.groups()
# ':' or '+' in the submodule name will certainly cause
# trouble when we create a remote name based on it, but
# this isn't an exhaustive check (FIXME)
raise Exception, "A remote name created from "+s+" is unlikely to be valid"
timestamp ="%Y%m%d%H%M%S")
for submodule, commit in submodules:
print "=== Doing", submodule
# Create temporary names for the remote and branch, taking very
# cursory steps to avoid collisions with existing branches /
# remotes:
remote_name = "submodule-"+submodule+"-"+timestamp
branch_name = "tmp-"+submodule+"-"+timestamp
# Find the URL of the submodule:
p = Popen(["git","config","-f",".gitmodules","--get",'submodule.'+submodule+'.url'],stdout=PIPE)
submodule_url = p.communicate()[0].decode().strip()
if p.returncode != 0:
raise Exception, "Getting the submodule URL failed"
print "Got submodule:", submodule, "at commit", commit, "with URL", submodule_url
# Add a remote for the submodule's URL:
# Fetch all the objects required for the submodule:
# Create a temporary branch based on the committed submodule version:
# Commit removal of the submodule:
check_call(["git","commit","-m","Removed the submodule "+submodule])
# Move the existing submodule out of the way:
# Merge in the branch to the subdirectory:
check_call(["git","commit","-m","Merge in "+submodule+" as a subdirectory"])
# Remove the branch and remote that the script created:
# Finally, get the subdirectory from the index:
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment