Skip to content
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
executable file 200 lines (158 sloc) 5.22 KB
#!/usr/bin/env python
#-*- coding: utf-8 -*-
import os, re, sys, shutil
from subprocess import Popen, PIPE
from optparse import OptionParser
from xml.dom import minidom
import unittest
class GitCpTest(unittest.TestCase):
def testCp(self):
def do(proc_args, message, options, input = None):
if message and options.verbose:
print "*** %s ***" % message
if options.verbose:
if input:
print 'stdin:'
print '\t' + input
print 'cmd:'
print '\t' + ' '.join(proc_args)
if not options.dry:
proc = Popen(proc_args, stderr=PIPE, stdout=PIPE, stdin=PIPE)
result, error = proc.communicate(input)
if options.verbose:
if result:
print 'stdout:'
print '\t' + result
if options.verbose:
if error:
print 'stderr:'
print '\t' + error
return result
def make_tree(path, tree, options):
global git_cmd
# Get the tree of HEAD
lstree = do(git_cmd + ['ls-tree', '-z', 'HEAD:' + path], None, options).strip().\
if lstree:
# Remove empty lines
lstree = [line for line in lstree if line]
# Parse each lines
lstree_pattern = '([0-9]*?) (blob|tree) ([0-9a-f]{40})\t(.*)'
files = [list(re.match(lstree_pattern, line).groups()) for line in lstree]
files = []
# Add new blob
for id in tree:
if tree[id][-1] == '/':
tree[id] = tree[id][:-1]
type = 'tree'
type = 'blob'
if type == 'tree':
mode = '040000'
mode = '100644'
# Remove duplicated file
files = [item for item in files if item[3] != tree[id]]
files.append([mode, type, id, tree[id]])
# Sort the blobs by filename
files.sort(None, lambda item: item[3] if item[1] == 'blob' else item[3] + '/')
# Formatting
files = [' '.join(item[:3]) + '\t' + item[3] for item in files]
# Make the tree
tree_id = do(git_cmd + ['mktree', '-z'], None, options, '\0'.join(files)).\
return tree_id
def commit(tree_id, message, options = None):
global git_cmd, git_dir
master = os.path.join(git_dir, 'refs', 'heads', 'master')
if os.path.isfile(master):
parent_id = open(master).read().strip()
if not parent_id:
raise Exception("Invalid HEAD")
parent_param = ['-p', parent_id]
parent_param = []
commit_id = do(git_cmd + ['commit-tree', tree_id] + parent_param +
['-m', message], None, options).strip()
open(master, 'w').write(commit_id)
return commit_id
def is_git_repo(path):
if os.path.isdir(path):
files = os.listdir(path)
return ('HEAD' in files) and ('objects' in files)
return False
def find_git_repo(path):
git_dir = os.path.normpath(path)
path_segments = []
while git_dir:
if is_git_repo(git_dir):
return git_dir, path_segments
segs = git_dir.rsplit(os.path.sep, 1)
git_dir = segs[0]
if len(segs) > 1:
path_segments = [segs[1]] + path_segments
def is_git_tree(path, options):
global git_cmd
# Get the tree of HEAD
lstree = do(git_cmd + ['ls-tree', '-z', 'HEAD:' + path], None, options).\
if lstree != ['']:
return True
return False
def make_trees(path_segments, tree, options):
# make parents of the tree object
while True:
tree_id = make_tree('/'.join(path_segments), tree, options)
if not tree_id:
if not path_segments:
seg = path_segments.pop()
tree = {tree_id: seg + '/'}
return tree_id
def cp(srcs, dest, options):
global git_dir, git_cmd
git_dir, path_segments = find_git_repo(dest)
git_cmd = ['git', '--git-dir=' + git_dir]
# Make a blob object and put it into tree map
tree = {}
for src in srcs:
blob_id = do(git_cmd + ['hash-object', '-t', 'blob', '-w', '--', src],
None, options).strip()
filename = os.path.basename(src)
tree[blob_id] = filename
git_path = '/'.join(path_segments)
if not is_git_tree(git_path, options):
seg = path_segments.pop()
tree[blob_id] = seg
tree_id = make_trees(path_segments, tree, options)
return 127
# commit
commit_id = commit(tree_id, 'copy ' + src, options)
return 0
def main():
""" main function """
parser = OptionParser()
parser.usage = \
'%prog [options]... <src> <dest>\n' + \
' or: %prog [options]... <src>... <directory>'
parser.add_option("-d", "--dry", action="store_true", dest="dry",
default=False, help="Dry run")
parser.add_option("-v", "--verbose", action="store_true", dest="verbose",
default=False, help="explain what is being done")
options, args = parser.parse_args()
options.dry = False
if len(args) > 1:
return cp(args[:-1], args[-1], options)
return 1
if __name__ == '__main__':
You can’t perform that action at this time.