#!/usr/bin/env python # new version, 030428 # we now check add/delete differently from the previous version. # We start with a list of modified files. "Add" is collected # by taking any files that are not currently in CVS repository. # "Delete" is collected by looking at the patch file and find files # whose deleted lines are equal the numbers of lines in existing files. # NOTE: # We decide whether a directory is in CVS based on whether it has # a CVS sub-directory. This is not super-safe and accurate, but # should be fine in all normal cases. # TODO: # Need to support '-R" option import sys; import os; import os.path; import commands; import string; import re; import time; def usage(): print print "cvspatch [options for 'patch'] "; print print " Execute this under CVS tree, it will apply patch and do "; print " corresponding CVS add/rm properly. "; print print " --dry-run option will list files that will be added or deleted"; print " without actually adding or deleting them." print print " --verbose will cause extra (time consuming) consistency checking."; print def parse_options(argv): global strip_level, dry_run, verbose; for i in range(1, len(argv)-1): if argv[i] == "--dry-run": dry_run = 1; if argv[i] == "--verbose": verbose=1; if argv[i][0:2] == "-p": if len(argv[i]) > 2 and argv[i][2] >= '0' and argv[i][2] <= '9': strip_level = int(argv[i][2]); else : i = i+1; strip_level = int(argv[i]); def cvs_file_exist(f): global verbose; output= commands.getoutput("cvs status " + f); temp=string.split(commands.getoutput("cvs status " + f)); for i in range(0, len(temp)): if temp[i] == "Status:": if temp[i+1] == "Unknown": return 0; elif temp[i+1] != "Up-to-date" and verbose != 0: print "Warning : unexpected cvs status for " + f + " - " + temp[i+1]; return 1; return 0; # if we can't find "Status" at all def check_parent_for_add(f): global adddirs, verbose; if not re.search(r'/', f): return; p = re.sub(r'/[^/]*$', '', f); if p in adddirs: # skip if it is already added return; check_parent_for_add(p); if not os.path.isdir(p + "/CVS") : adddirs.append(p); def strip_file(fname, level): for j in range(0, level): fname = re.sub(r'^[^/]*/', '', fname); return fname; def parse_patch_file(pfile): global files, addfiles, deletefiles, strip_level; f=open(pfile, "r"); fname1=""; fname2=""; while (1==1): line=f.readline(); if not line: break; if re.match(r'\-\-\- ', line) : assert(fname2==""); fname1=string.split(line)[1]; continue; if re.match(r'\+\+\+ ', line) : assert(fname1 != ""); fname2=string.split(line)[1]; else: continue; if (fname2 != "/dev/null"): fname = fname2; else: assert(fname1 != "/dev/null"); fname = fname1; fname1=""; fname2=""; fname=strip_file(fname, strip_level); files.append(fname); nextline = f.readline(); if re.search(r'-0,0', nextline): check_parent_for_add(fname); addfiles.append(fname); if re.search(r'\+0,0', nextline): deletefiles.append(fname); f.close(); def verbose_check_files(): global files, addfiles, deletefiles; for i in files: time.sleep(1); # os.system("usleep 250"); print "checking " + i + " ..."; ret=cvs_file_exist(i); if i in addfiles: if ret : print "\tto-be-added file already exists in CVS - " + i; else : if not ret : print "\tto-be-modifed file does not exist in CVS - " + i; def print_list(prompt, list): print prompt; for i in list: print i; def do_report(): global files, addfiles, deletefiles, adddirs; print "New directories"; for i in adddirs: print " ", i; print "Modified files (+: newly added; -: removed) "; for i in files: if i in addfiles: print "+ ", elif i in deletefiles: print "- ", else : print " ", print i; def get_prompt(prompt): print prompt; sys.stdout.flush(); sys.stdin.readline(); # ========================== MAIN ========================= # figure out options if len(sys.argv)==1 : usage() sys.exit() strip_level=0; dry_run=0; verbose=0; parse_options(sys.argv); args = string.join(sys.argv[1:-1]); # --verbose for patch is actually boring args = re.sub(r'--verbose', '', args); # obtain a list of patch_file=sys.argv[len(sys.argv)-1]; #files=string.split(commands.getoutput("lsdiff " + patch_file)); # experiment files=[]; addfiles=[]; deletefiles=[]; adddirs=[]; parse_patch_file(patch_file); # verbose checking if verbose: verbose_check_files(); # report actions to be taken do_report(); if not dry_run: get_prompt("Press to start patching the tree ... "); else: print; print "Start patching the tree ..."; # do the patching os.system("patch " + args + " < " + patch_file); sys.stdout.flush(); # add/remove files if not dry_run: # another chance get_prompt("Press to start CVS adding/deleting ... "); for i in adddirs: print "adding directory " + i + " ... "; os.system("cvs add " + i); sys.stdout.flush(); for i in addfiles: print "adding file " + i + " ... "; os.system("cvs add " + i); sys.stdout.flush(); for i in deletefiles: print "deleting file " + i + " ... "; if os.path.exists(i): print "\tWarning : deleted file still exists! Patch failure?!"; os.system("cvs rm -f " + i); sys.stdout.flush();