def execute(self, args): try: clonePath = git.baseDir() if clonePath == "": return False except git.GrapeGitError: pass clonePath = args["--source"] if not clonePath: clonePath = utility.userInput("Enter path to original clone", clonePath) newTree = args["--dest"] if not newTree: newTree = utility.userInput("Enter name of new working tree") newTreePath = args["--destPath"] if not newTreePath: newTreePath = utility.userInput("Enter desired location of new working tree (must exist)", os.path.realpath(os.path.join(clonePath, "../"))) newRepo = os.path.join(newTreePath, newTree) #TODO: When grape is installed to PUBLIC, the first argument here should be the # publically available git-new-workdir, instead of the version in the local repo. p = utility.executeSubProcess(os.path.join(os.path.dirname(__file__), "..", "git-new-workdir") + " " + clonePath + " " + newRepo, workingDirectory=os.getcwd()) p.wait() os.chdir(newRepo) if not args["--noSparse"]: print "created new working tree %s in %s. Calling grape uv from new workspace now." % (newTree, newTreePath) menu = grapeMenu.menu() return menu.applyMenuChoice('uv', ["uv"] + args["<uvargs>"]) else: git.checkout("HEAD") return True
def execute(self, args): commitargs = "" if args['-a']: commitargs = commitargs + " -a" elif args["<filetree>"]: commitargs = commitargs + " %s" % args["<filetree>"] if not args['-m']: args["-m"] = utility.userInput("Please enter commit message:") commitargs += " -m \"%s\"" % args["-m"] wsDir = utility.workspaceDir() os.chdir(wsDir) submodules = [(True, x) for x in git.getModifiedSubmodules()] subprojects = [(False, x) for x in grapeConfig.GrapeConfigParser. getAllActiveNestedSubprojectPrefixes()] for stage, sub in submodules + subprojects: os.chdir(os.path.join(wsDir, sub)) subStatus = git.status("--porcelain -uno") if subStatus: utility.printMsg("Committing in %s..." % sub) if self.commit(commitargs, sub) and stage: os.chdir(wsDir) utility.printMsg("Staging committed change in %s..." % sub) git.add(sub) os.chdir(wsDir) if submodules or git.status("--porcelain"): utility.printMsg("Performing commit in outer level project...") self.commit(commitargs, wsDir) return True
def execute(self, args): commitargs = "" if args['-a']: commitargs = commitargs + " -a" elif args["<filetree>"]: commitargs = commitargs + " %s"% args["<filetree>"] if not args['-m']: args["-m"] = utility.userInput("Please enter commit message:") commitargs += " -m \"%s\"" % args["-m"] wsDir = utility.workspaceDir() os.chdir(wsDir) submodules = [(True, x ) for x in git.getModifiedSubmodules()] subprojects = [(False, x) for x in grapeConfig.GrapeConfigParser.getAllActiveNestedSubprojectPrefixes()] for stage,sub in submodules + subprojects: os.chdir(os.path.join(wsDir,sub)) subStatus = git.status("--porcelain -uno") if subStatus: utility.printMsg("Committing in %s..." % sub) if self.commit(commitargs, sub) and stage: os.chdir(wsDir) utility.printMsg("Staging committed change in %s..." % sub) git.add(sub) os.chdir(wsDir) if submodules or git.status("--porcelain"): utility.printMsg("Performing commit in outer level project...") self.commit(commitargs, wsDir) return True
def handleEnsureLocalUpToDateMRE(mre): _pushBranch = False _skipPush = False cleanupPushArgs = [] for e1, repo, branch in zip(mre.exceptions(), mre.repos(), mre.branches()): try: raise e1 except git.GrapeGitError as e: if ("[rejected]" in e.gitOutput and "(non-fast-forward)" in e.gitOutput ) or "Couldn't find remote ref" in e.gitOutput: if "Couldn't find remote ref" in e.gitOutput: if not _pushBranch: utility.printMsg( "No remote reference to %s in %s's origin. You may want to push this branch." % (branch, repo)) else: utility.printMsg( "Fetch of %s rejected as non-fast-forward in repo %s" % (branch, repo)) pushBranch = _pushBranch if _skipPush: pushBranch = False elif not pushBranch: pushBranch = utility.userInput( "Would you like to push your local branch? \n" "(select 'a' to say yes for (a)ll subprojects, 's' to (s)kip push for all subprojects)" "\n(y,n,a,s)", 'y') if str(pushBranch).lower()[0] == 'a': _pushBranch = True pushBranch = True if str(pushBranch).lower()[0] == 's': _skipPush = True pushBranch = False if pushBranch: cleanupPushArgs.append((repo, branch, None)) else: utility.printMsg("Skipping push of local %s in %s" % (branch, repo)) elif e.commError: utility.printMsg( "Could not update %s from origin due to a connectivity issue. Checking out most recent\n" "local version. " % branch) else: raise (e) # do another MRC launch to do any follow up pushes that were requested. utility.MultiRepoCommandLauncher( cleanupPush, listOfRepoBranchArgTuples=cleanupPushArgs).launchFromWorkspaceDir( handleMRE=handleCleanupPushMRE) return
def execute(self, args): start = args["--start"] if not start: start = self._public # decide whether to recurse recurse = grapeConfig.grapeConfig().get('workspace', 'manageSubmodules') if args["--recurse"]: recurse = True if args["--noRecurse"]: recurse = False if not args["<descr>"]: args["<descr>"] = utility.userInput( "Enter one word description for branch:") if not args["--user"]: args["--user"] = utility.getUserName() branchName = self._key + "/" + args["--user"] + "/" + args["<descr>"] launcher = utility.MultiRepoCommandLauncher(createBranch, runInSubmodules=recurse, runInSubprojects=recurse, runInOuter=True, branch=start, globalArgs=branchName) launcher.initializeCommands() utility.printMsg("About to create the following branches:") for repo, branch in zip(launcher.repos, launcher.branches): utility.printMsg("\t%s off of %s in %s" % (branchName, branch, repo)) proceed = utility.userInput("Proceed? [y/n]", default="y") if proceed: grapeMenu.menu().applyMenuChoice( 'up', ['up', '--public=%s' % start]) launcher.launchFromWorkspaceDir() else: utility.printMsg("branches not created")
def execute(self, args): branch = args["<branch>"] force = args["-D"] if not branch: branch = utility.userInput("Enter name of branch to delete") if args["--verify"]: proceed = utility.userInput( "Would you like to delete the branch %s" % branch, 'y') if not proceed: return True launcher = utility.MultiRepoCommandLauncher(deleteBranch, branch=branch, globalArgs=[force]) try: launcher.launchFromWorkspaceDir() except utility.MultiRepoException as e: handleDeleteBranchMRE(e, force) return True
def execute(self, args): start = args["--start"] if not start: start = self._public # decide whether to recurse recurse = grapeConfig.grapeConfig().get('workspace', 'manageSubmodules') if args["--recurse"]: recurse = True if args["--noRecurse"]: recurse = False if not args["<descr>"]: args["<descr>"] = utility.userInput("Enter one word description for branch:") if not args["--user"]: args["--user"] = utility.getUserName() branchName = self._key + "/" + args["--user"] + "/" + args["<descr>"] launcher = utility.MultiRepoCommandLauncher(createBranch, runInSubmodules=recurse, runInSubprojects=recurse, runInOuter=True, branch=start, globalArgs=branchName) launcher.initializeCommands() utility.printMsg("About to create the following branches:") for repo, branch in zip(launcher.repos, launcher.branches): utility.printMsg("\t%s off of %s in %s" % (branchName, branch, repo)) proceed = utility.userInput("Proceed? [y/n]", default="y") if proceed: grapeMenu.menu().applyMenuChoice('up', ['up', '--public=%s' % start]) launcher.launchFromWorkspaceDir() else: utility.printMsg("branches not created")
def execute(self,args): if not "<<cmd>>" in args: args["<<cmd>>"] = "mr" otherBranch = args['<branch>'] if not otherBranch: # list remote branches that are available print git.branch('-r') otherBranch = utility.userInput("Enter name of branch you would like to merge into this branch (without the origin/ prefix)") # make sure remote references are up to date utility.printMsg("Fetching remote references in all projects...") try: utility.MultiRepoCommandLauncher(fetchHelper).launchFromWorkspaceDir() except utility.MultiRepoException as mre: commError = False commErrorRepos = [] for e, r in zip(mre.exceptions(), mre.repos()): if e.commError: commErrorRepos.append(r) commError = True if commError: utility.printMsg("ERROR: can't communicate with remotes for %s. Halting remote merge." % commErrorRepos) return False # update our local reference to the remote branch so long as it's fast-forwardable or we don't have it yet..) hasRemote = ("origin/%s" % otherBranch) in git.remoteBranches() hasBranch = git.hasBranch(otherBranch) currentBranch = git.currentBranch() remoteUpToDateWithLocal = git.branchUpToDateWith("remotes/origin/%s" % otherBranch, otherBranch) updateLocal = hasRemote and (remoteUpToDateWithLocal or not hasBranch) and currentBranch != otherBranch if updateLocal: utility.printMsg("updating local branch %s from %s" % (otherBranch, "origin/%s" % otherBranch)) utility.MultiRepoCommandLauncher(updateBranchHelper, branch=otherBranch).launchFromWorkspaceDir(handleMRE=updateBranchHandleMRE) args["<branch>"] = otherBranch if updateLocal else "origin/%s" % otherBranch # we've handled the update, we don't want m or md to update the local branch. args["--noUpdate"] = True # if mr is called by the user, need to initialize the --continue argument. # if it is called by md, it will be set already. if not "--continue" in args: args["--continue"] = False return grapeMenu.menu().getOption('m').execute(args)
def handleEnsureLocalUpToDateMRE(mre): _pushBranch = False _skipPush = False cleanupPushArgs = [] for e1, repo, branch in zip(mre.exceptions(), mre.repos(), mre.branches()): try: raise e1 except git.GrapeGitError as e: if ("[rejected]" in e.gitOutput and "(non-fast-forward)" in e.gitOutput) or "Couldn't find remote ref" in e.gitOutput: if "Couldn't find remote ref" in e.gitOutput: if not _pushBranch: utility.printMsg("No remote reference to %s in %s's origin. You may want to push this branch." % (branch, repo)) else: utility.printMsg("Fetch of %s rejected as non-fast-forward in repo %s" % (branch, repo)) pushBranch = _pushBranch if _skipPush: pushBranch = False elif not pushBranch: pushBranch = utility.userInput("Would you like to push your local branch? \n" "(select 'a' to say yes for (a)ll subprojects, 's' to (s)kip push for all subprojects)" "\n(y,n,a,s)", 'y') if str(pushBranch).lower()[0] == 'a': _pushBranch = True pushBranch = True if str(pushBranch).lower()[0] == 's': _skipPush = True pushBranch = False if pushBranch: cleanupPushArgs.append((repo, branch, None)) else: utility.printMsg("Skipping push of local %s in %s" % (branch, repo)) elif e.commError: utility.printMsg("Could not update %s from origin due to a connectivity issue. Checking out most recent\n" "local version. " % branch) else: raise(e) # do another MRC launch to do any follow up pushes that were requested. utility.MultiRepoCommandLauncher(cleanupPush, listOfRepoBranchArgTuples=cleanupPushArgs).launchFromWorkspaceDir(handleMRE=handleCleanupPushMRE) return
def execute(self, args): # this is necessary due to the unholy relationships between mr, m, and md. if not "<<cmd>>" in args: args["<<cmd>>"] = 'm' otherBranch = args["<branch>"] if args["<branch>"] else utility.userInput("Enter name of branch you would like" " to merge into this branch") args["<branch>"] = otherBranch config = grapeConfig.grapeConfig() publicBranches = config.getPublicBranchList() toks = otherBranch.split("origin/") if toks[-1] in publicBranches: public = toks[-1] publicMapping = config.getMapping("workspace", "submodulePublicMappings") subpublic = publicMapping[public] toks[-1] = subpublic subpublic = 'origin/'.join(toks) else: subpublic = otherBranch mdArgs = {} mdArgs["--am"] = args["--am"] mdArgs["--as"] = args["--as"] mdArgs["--at"] = args["--at"] mdArgs["--aT"] = args["--aT"] mdArgs["--ay"] = args["--ay"] mdArgs["--aY"] = args["--aY"] mdArgs["--askAll"] = args["--askAll"] mdArgs["--public"] = args["<branch>"] mdArgs["--subpublic"] = subpublic mdArgs["--recurse"] = not args["--noRecurse"] mdArgs["--noRecurse"] = args["--noRecurse"] mdArgs["--continue"] = args["--continue"] mdArgs["<<cmd>>"] = args["<<cmd>>"] mdArgs["--noUpdate"] = args["--noUpdate"] mdArgs["--squash"] = args["--squash"] return grapeMenu.menu().getOption("md").execute(mdArgs)
def execute(self, args): # this is necessary due to the unholy relationships between mr, m, and md. if not "<<cmd>>" in args: args["<<cmd>>"] = 'm' otherBranch = args["<branch>"] if args[ "<branch>"] else utility.userInput( "Enter name of branch you would like" " to merge into this branch") args["<branch>"] = otherBranch config = grapeConfig.grapeConfig() publicBranches = config.getPublicBranchList() toks = otherBranch.split("origin/") if toks[-1] in publicBranches: public = toks[-1] publicMapping = config.getMapping("workspace", "submodulePublicMappings") subpublic = publicMapping[public] toks[-1] = subpublic subpublic = 'origin/'.join(toks) else: subpublic = otherBranch mdArgs = {} mdArgs["--am"] = args["--am"] mdArgs["--as"] = args["--as"] mdArgs["--at"] = args["--at"] mdArgs["--aT"] = args["--aT"] mdArgs["--ay"] = args["--ay"] mdArgs["--aY"] = args["--aY"] mdArgs["--askAll"] = args["--askAll"] mdArgs["--public"] = args["<branch>"] mdArgs["--subpublic"] = subpublic mdArgs["--recurse"] = not args["--noRecurse"] mdArgs["--noRecurse"] = args["--noRecurse"] mdArgs["--continue"] = args["--continue"] mdArgs["<<cmd>>"] = args["<<cmd>>"] mdArgs["--noUpdate"] = args["--noUpdate"] mdArgs["--squash"] = args["--squash"] return grapeMenu.menu().getOption("md").execute(mdArgs)
def execute(self, args): sync = args["--sync"].lower().strip() sync = sync == "true" or sync == "yes" args["--sync"] = sync checkoutargs = '' branch = args["<branch>"] if args['-b']: checkoutargs += " -b" workspaceDir = utility.workspaceDir() os.chdir(workspaceDir) currentSHA = git.shortSHA("HEAD") utility.printMsg("Performing checkout of %s in outer level project." % branch) launcher = utility.MultiRepoCommandLauncher(handledCheckout, listOfRepoBranchArgTuples=[(workspaceDir, branch, [checkoutargs, sync])]) if not launcher.launchFromWorkspaceDir(handleMRE=handleCheckoutMRE)[0]: return False previousSHA = currentSHA submoduleListDidChange = ".gitmodules" in git.diff("--name-only %s %s" % (previousSHA, branch)) addedModules = [] removedModules = [] uvArgs = [] submodulesDidChange = False if submoduleListDidChange and grapeConfig.grapeConfig().getboolean("workspace", "manageSubmodules"): self.parseGitModulesDiffOutput(git.diff("%s %s --no-ext-diff -- .gitmodules" % (previousSHA, branch)), addedModules, removedModules) if not addedModules and not removedModules: pass else: submodulesDidChange = True if removedModules: for sub in removedModules: try: os.chdir(os.path.join(workspaceDir, sub)) if git.isWorkingDirectoryClean(): cleanBehaviorSet = args["--noUpdateView"] or args["--updateView"] if not cleanBehaviorSet: clean = utility.userInput("Would you like to remove the submodule %s ?" % sub, 'n') elif args["--noUpdateView"]: clean = False elif args["--updateView"]: clean = True if clean: utility.printMsg("Removing clean submodule %s." % sub) os.chdir(workspaceDir) shutil.rmtree(os.path.join(workspaceDir, sub)) else: utility.printMsg("Unstaged / committed changes in %s, not removing." % sub) os.chdir(workspaceDir) except OSError: pass # check to see if nested project list changed addedProjects = [] removedProjects = [] removedProjectPrefices = {} nestedProjectListDidChange = False os.chdir(workspaceDir) if ".grapeconfig" in git.diff("--name-only %s %s" % (previousSHA, branch)): configDiff = git.diff("--no-ext-diff %s %s -- %s" % (previousSHA, branch, ".grapeconfig")) nestedProjectListDidChange = "[nestedprojects]" in configDiff.lower() self.parseGrapeConfigNestedProjectDiffOutput(configDiff, addedProjects, removedProjects, removedProjectPrefices) if removedProjects: config = grapeConfig.grapeConfig() for proj in removedProjects: projPrefix = removedProjectPrefices[proj] try: os.chdir(os.path.join(workspaceDir, proj)) except OSError as e: if e.errno == 2: # directory doesn't exist, that's OK since we're thinking about removing it # anyways at this point... continue if git.isWorkingDirectoryClean(): removeBehaviorSet = args["--noUpdateView"] or args["--updateView"] if not removeBehaviorSet: remove = utility.userInput("Would you like to remove the nested subproject %s? \n" "All work that has not been pushed will be lost. " % projPrefix, 'n' ) elif args["--noUpdateView"]: remove = False elif args["--updateView"]: remove = True if remove: remove = utility.userInput("Are you sure you want to remove %s? When you switch back to the previous branch, you will have to\n" "reclone %s." % (projPrefix, projPrefix), 'n') if remove: os.chdir(workspaceDir) shutil.rmtree(os.path.join(workspaceDir,projPrefix)) else: utility.printMsg("Unstaged / committed changes in %s, not removing. \n" "Note this project is NOT active in %s. " % (projPrefix, branch)) os.chdir(workspaceDir) if not submodulesDidChange and not nestedProjectListDidChange: uvArgs.append("--checkSubprojects") else: updateViewSet = args["--noUpdateView"] or args["--updateView"] if not updateViewSet: updateView = utility.userInput("Submodules or subprojects were added/removed as a result of this checkout. \n" + "%s" % ("Added Projects: %s\n" % ','.join(addedProjects) if addedProjects else "") + "%s" % ("Added Submodules: %s\n"% ','.join(addedModules) if addedModules else "") + "%s" % ("Removed Projects: %s\n" % ','.join(removedProjects) if removedProjects else "") + "%s" % ("Removed Submodules: %s\n" % ','.join(removedModules) if removedModules else "") + "Would you like to update your workspace view? [y/n]", 'n') elif args["--noUpdateView"]: updateView = False elif args["--updateView"]: updateView = True if not updateView: uvArgs.append("--checkSubprojects") if args["-b"]: uvArgs.append("-b") if sync: uvArgs.append("--sync=True") else: uvArgs.append("--sync=False") # in case the user switches to a branch without corresponding branches in the submodules, make sure active submodules # are at the right commit before possibly creating new branches at the current HEAD. git.submodule("update") utility.printMsg("Calling grape uv %s to ensure branches are consistent across all active subprojects and submodules." % ' '.join(uvArgs)) grapeConfig.read() grapeMenu.menu().applyMenuChoice('uv', uvArgs) os.chdir(workspaceDir) if sync: utility.printMsg("Switched to %s. Updating from remote...\n\t (use --sync=False or .grapeconfig.post-checkout.syncWithOrigin to change behavior.)" % branch) if args["-b"]: grapeMenu.menu().applyMenuChoice("push") else: grapeMenu.menu().applyMenuChoice("pull") else: utility.printMsg("Switched to %s." % branch) global _skipBranchCreation global _createNewBranch _skipBranchCreation = False _createNewBranch = False return True
def execute(self, args): name = args["--name"] prefix = args["--prefix"] url = args["--url"] fullurl = utility.parseSubprojectRemoteURL(url) branch = args["--branch"] config = grapeConfig.grapeConfig() projectType = self.parseSubprojectType(config, args) proceed = args["--noverify"] if projectType == "subtree": # whether or not to squash squash = args["--squash"] or config.get("subtrees", "mergePolicy").strip().lower() == "squash" squash = squash and not args["--nosquash"] squash_arg = "--squash" if squash else "" # expand the URL if not proceed: proceed = utility.userInput("About to create a subtree called %s at path %s,\n" "cloned from %s at %s " % (name, prefix, fullurl, branch) + ("using a squash merge." if squash else "") + "\nProceed? [y/n]", "y") if proceed: os.chdir(utility.workspaceDir()) git.subtree("add %s --prefix=%s %s %s" % (squash_arg, prefix, fullurl, branch)) #update the configuration file current_cfg_names = config.get("subtrees", "names").split() if not current_cfg_names or current_cfg_names[0].lower() == "none": config.set("subtrees", "names", name) else: current_cfg_names.append(name) config.set("subtrees", "names", ' '.join(current_cfg_names)) section = "subtree-%s" % name config.add_section(section) config.set(section, "prefix", prefix) config.set(section, "remote", url) config.set(section, "topicPrefixMappings", "?:%s" % branch) with open(os.path.join(utility.workspaceDir(), ".grapeconfig"), "w") as f: config.write(f) utility.printMsg("Successfully added subtree branch. \n" "Updated .grapeconfig file. Review changes and then commit. ") elif projectType == "submodule": if not proceed: proceed = utility.userInput("about to add %s as a submodule at path %s,\n" "cloned from %s at branch %s.\nproceed? [y/n]" % (name, prefix, url, branch), "y") if proceed: git.submodule("add --name %s --branch %s %s %s" % (name, branch, url, prefix)) print("Successfully added submodule %s at %s. Please review changes and commit." % (name, prefix)) elif projectType == "nested": if not proceed: proceed = utility.userInput(" about to clone %s as a nested git repo at path %s,\n" "cloned from %s at branch %s.\nProceed? [y/n]" % (name, prefix, url, branch), 'y') if proceed: git.clone("%s %s" % (fullurl, prefix)) ignorePath = os.path.join(git.baseDir(), ".gitignore") with open(ignorePath, 'a') as ignore: ignore.writelines([prefix+'\n']) git.add(ignorePath) wsConfig = grapeConfig.workspaceConfig() currentSubprojects = wsConfig.getList("nestedProjects", "names") currentSubprojects.append(name) wsConfig.set("nestedProjects", "names", ' '.join(currentSubprojects)) newSection = "nested-%s" % name wsConfig.ensureSection(newSection) wsConfig.set(newSection, "prefix", prefix) wsConfig.set(newSection, "url", url) configFileName = os.path.join(utility.workspaceDir(), ".grapeconfig") with open(os.path.join(configFileName), 'w') as f: wsConfig.write(f) git.add(configFileName) git.commit("%s %s -m \"GRAPE: Added nested subproject %s\"" % (ignorePath, configFileName, prefix)) # update the runtime config with the new workspace .grapeconfig's settings. grapeConfig.read() userConfig = grapeConfig.grapeUserConfig() userConfig.ensureSection(newSection) userConfig.set(newSection, "active", "True") grapeConfig.writeConfig(userConfig, os.path.join(utility.workspaceDir(), ".git", ".grapeuserconfig")) return True
def handleCheckoutMRE(mre): global _skipBranchCreation global _createNewBranch newBranchReposArgTuples = [] newBranches = [] for e1, branch, project, checkoutargs in zip(mre.exceptions(), mre.branches(), mre.repos(), mre.args()): try: raise e1 except git.GrapeGitError as e: with utility.cd(project): if "pathspec" in e.gitOutput: createNewBranch = _createNewBranch if _skipBranchCreation: utility.printMsg("Skipping checkout of %s in %s" % (branch, project)) createNewBranch = False elif not createNewBranch: createNewBranch = utility.userInput( "Branch not found locally or remotely. Would you like to create a " "new branch called %s in %s? \n" "(select 'a' to say yes for (a)ll, 's' to (s)kip creation for branches that don't exist )" "\n(y,n,a,s)" % (branch, project), 'y') if str(createNewBranch).lower()[0] == 'a': _createNewBranch = True createNewBranch = True if str(createNewBranch).lower()[0] == 's': _skipBranchCreation = True createNewBranch = False if createNewBranch: newBranchReposArgTuples.append((project, branch, { "checkout": checkoutargs[0] })) else: continue elif "already exists" in e.gitOutput: utility.printMsg("Branch %s already exists in %s." % (branch, project)) branchDescription = git.commitDescription(branch) headDescription = git.commitDescription("HEAD") if branchDescription == headDescription: utility.printMsg( "Branch %s and HEAD are the same. Switching to %s." % (branch, branch)) action = "k" else: utility.printMsg( "Branch %s and HEAD are not the same." % branch) action = '' valid = False while not valid: action = utility.userInput( "Would you like to \n (k)eep it as is at: %s \n" " or \n (f)orce it to: %s? \n(k,f)" % (branchDescription, headDescription), 'k') valid = (action == 'k') or (action == 'f') if not valid: utility.printMsg( "Invalid input. Enter k or f. ") if action == 'k': git.checkout(branch) elif action == 'f': git.checkout("-B %s" % branch) elif "conflict" in e.gitOutput.lower(): utility.printMsg( "CONFLICT occurred when pulling %s from origin." % branch) elif "does not appear to be a git repository" in e.gitOutput.lower( ): utility.printMsg( "Remote 'origin' does not exist. " "This branch was not updated from a remote repository." ) elif "Couldn't find remote ref" in e.gitOutput: utility.printMsg( "Remote of %s does not have reference to %s. You may want to push this branch. " % (project, branch)) else: raise e if len(newBranchReposArgTuples) > 0: utility.MultiRepoCommandLauncher( createNewBranches, listOfRepoBranchArgTuples=newBranchReposArgTuples ).launchFromWorkspaceDir(handleMRE=createNewBranchesMREHandler)
def execute(self, args): sync = args["--sync"].lower().strip() sync = sync == "true" or sync == "yes" args["--sync"] = sync checkoutargs = '' branch = args["<branch>"] if args['-b']: checkoutargs += " -b" workspaceDir = utility.workspaceDir() os.chdir(workspaceDir) currentSHA = git.shortSHA("HEAD") utility.printMsg("Performing checkout of %s in outer level project." % branch) launcher = utility.MultiRepoCommandLauncher(handledCheckout, listOfRepoBranchArgTuples=[ (workspaceDir, branch, [checkoutargs, sync]) ]) if not launcher.launchFromWorkspaceDir(handleMRE=handleCheckoutMRE)[0]: return False previousSHA = currentSHA submoduleListDidChange = ".gitmodules" in git.diff( "--name-only %s %s" % (previousSHA, branch)) addedModules = [] removedModules = [] uvArgs = [] submodulesDidChange = False if submoduleListDidChange and grapeConfig.grapeConfig().getboolean( "workspace", "manageSubmodules"): self.parseGitModulesDiffOutput( git.diff("%s %s --no-ext-diff -- .gitmodules" % (previousSHA, branch)), addedModules, removedModules) if not addedModules and not removedModules: pass else: submodulesDidChange = True if removedModules: for sub in removedModules: try: os.chdir(os.path.join(workspaceDir, sub)) if git.isWorkingDirectoryClean(): cleanBehaviorSet = args[ "--noUpdateView"] or args["--updateView"] if not cleanBehaviorSet: clean = utility.userInput( "Would you like to remove the submodule %s ?" % sub, 'n') elif args["--noUpdateView"]: clean = False elif args["--updateView"]: clean = True if clean: utility.printMsg( "Removing clean submodule %s." % sub) os.chdir(workspaceDir) shutil.rmtree( os.path.join(workspaceDir, sub)) else: utility.printMsg( "Unstaged / committed changes in %s, not removing." % sub) os.chdir(workspaceDir) except OSError: pass # check to see if nested project list changed addedProjects = [] removedProjects = [] removedProjectPrefices = {} nestedProjectListDidChange = False os.chdir(workspaceDir) if ".grapeconfig" in git.diff("--name-only %s %s" % (previousSHA, branch)): configDiff = git.diff("--no-ext-diff %s %s -- %s" % (previousSHA, branch, ".grapeconfig")) nestedProjectListDidChange = "[nestedprojects]" in configDiff.lower( ) self.parseGrapeConfigNestedProjectDiffOutput( configDiff, addedProjects, removedProjects, removedProjectPrefices) if removedProjects: config = grapeConfig.grapeConfig() for proj in removedProjects: projPrefix = removedProjectPrefices[proj] try: os.chdir(os.path.join(workspaceDir, proj)) except OSError as e: if e.errno == 2: # directory doesn't exist, that's OK since we're thinking about removing it # anyways at this point... continue if git.isWorkingDirectoryClean(): removeBehaviorSet = args["--noUpdateView"] or args[ "--updateView"] if not removeBehaviorSet: remove = utility.userInput( "Would you like to remove the nested subproject %s? \n" "All work that has not been pushed will be lost. " % projPrefix, 'n') elif args["--noUpdateView"]: remove = False elif args["--updateView"]: remove = True if remove: remove = utility.userInput( "Are you sure you want to remove %s? When you switch back to the previous branch, you will have to\n" "reclone %s." % (projPrefix, projPrefix), 'n') if remove: os.chdir(workspaceDir) shutil.rmtree( os.path.join(workspaceDir, projPrefix)) else: utility.printMsg( "Unstaged / committed changes in %s, not removing. \n" "Note this project is NOT active in %s. " % (projPrefix, branch)) os.chdir(workspaceDir) if not submodulesDidChange and not nestedProjectListDidChange: uvArgs.append("--checkSubprojects") else: updateViewSet = args["--noUpdateView"] or args["--updateView"] if not updateViewSet: updateView = utility.userInput( "Submodules or subprojects were added/removed as a result of this checkout. \n" + "%s" % ("Added Projects: %s\n" % ','.join(addedProjects) if addedProjects else "") + "%s" % ("Added Submodules: %s\n" % ','.join(addedModules) if addedModules else "") + "%s" % ("Removed Projects: %s\n" % ','.join(removedProjects) if removedProjects else "") + "%s" % ("Removed Submodules: %s\n" % ','.join(removedModules) if removedModules else "") + "Would you like to update your workspace view? [y/n]", 'n') elif args["--noUpdateView"]: updateView = False elif args["--updateView"]: updateView = True if not updateView: uvArgs.append("--checkSubprojects") if args["-b"]: uvArgs.append("-b") if sync: uvArgs.append("--sync=True") else: uvArgs.append("--sync=False") # in case the user switches to a branch without corresponding branches in the submodules, make sure active submodules # are at the right commit before possibly creating new branches at the current HEAD. git.submodule("update") utility.printMsg( "Calling grape uv %s to ensure branches are consistent across all active subprojects and submodules." % ' '.join(uvArgs)) grapeConfig.read() grapeMenu.menu().applyMenuChoice('uv', uvArgs) os.chdir(workspaceDir) if sync: utility.printMsg( "Switched to %s. Updating from remote...\n\t (use --sync=False or .grapeconfig.post-checkout.syncWithOrigin to change behavior.)" % branch) if args["-b"]: grapeMenu.menu().applyMenuChoice("push") else: grapeMenu.menu().applyMenuChoice("pull") else: utility.printMsg("Switched to %s." % branch) global _skipBranchCreation global _createNewBranch _skipBranchCreation = False _createNewBranch = False return True
def mergeIntoCurrent(self, branchName, args, projectName): choice = False strategy = None if args["--continue"]: if self.continueLocalMerge(args): return True if args['--am']: strategy = 'am' elif args['--as']: strategy = 'as' elif args['--at']: strategy = 'at' elif args['--aT']: strategy = 'aT' elif args['--ay']: strategy = 'ay' elif args['--aY']: strategy = 'aY' if not strategy or args['--askAll']: repoSpec = " in %s" % projectName if args['--askAll'] else "" strategy = utility.userInput("How do you want to resolve changes%s? [am / as / at / aT / ay / aY] \n" % repoSpec + "am: Auto Merge (default) \n" + "as: Safe Merge - issues conflicts if both branches touch same file.\n" + "at: Accept Theirs - accept changes in %s if both branches touch same file\n" % branchName + "aT: Accept Theirs (if conflicted) - resolves conflicts by accepting changes in %s\n" % branchName + "ay: Accept Yours - accept changes in current branch if both branches touch same file\n" + "aY: Accept Yours (if conflicted) - resolves conflicts by using changes in current branch.", "am") if strategy == 'am': args["--am"] = True utility.printMsg("Merging using git's default strategy...") choice = self.merge(branchName, "", args) elif strategy == 'as' or strategy == 'at' or strategy == 'ay': if strategy == 'as': args["--as"] = True # this employs using the custom low-level merge driver "verify" and # appending a "* merge=verify" to the .gitattributes file. # # see # http://stackoverflow.com/questions/5074452/git-how-to-force-merge-conflict-and-manual-merge-on-selected-file # for details. utility.printMsg("Merging forcing conflicts whenever both branches edited the same file...") elif strategy == 'at': args["--at"] = True elif strategy == 'ay': args["--ay"] = True base = git.gitDir() if base == "": return False attributes = os.path.join(base, ".gitattributes") tmpattributes = None if os.path.exists(attributes): tmpattributes = os.path.join(base, ".gitattributes.tmp") # save original attributes file shutil.copyfile(attributes, tmpattributes) #append merge driver strategy to the attributes file with open(attributes, 'a') as f: f.write("* merge=verify") else: with open(attributes, 'w') as f: f.write("* merge=verify") # perform the merge choice = self.merge(branchName, "", args) # restore original attributes file if tmpattributes: shutil.copyfile(tmpattributes, attributes) os.remove(tmpattributes) else: os.remove(attributes) elif strategy == 'aT': args["--aT"] = True utility.printMsg("Merging using recursive strategy, resolving conflicts cleanly with changes in %s..." % branchName) choice = self.merge(branchName, "-Xtheirs", args) elif strategy == 'aY': args["--aY"] = True utility.printMsg("Merging using recursive strategy, resolving conflicts cleanly with current branch's changes...") choice = self.merge(branchName, "-Xours", args) return choice
def defineActiveSubmodules(projectType="submodule"): """ Queries the user for the submodules (projectType == "submodule") or nested subprojects (projectType == "nested subproject") they would like to activate. """ if projectType == "submodule": allSubprojects = git.getAllSubmodules() activeSubprojects = git.getActiveSubmodules() if projectType == "nested subproject": config = grapeConfig.grapeConfig() allSubprojectNames = config.getAllNestedSubprojects() allSubprojects = [] for project in allSubprojectNames: allSubprojects.append( config.get("nested-%s" % project, "prefix")) activeSubprojects = grapeConfig.GrapeConfigParser.getAllActiveNestedSubprojectPrefixes( ) toplevelDirs = {} toplevelActiveDirs = {} toplevelSubs = [] for sub in allSubprojects: # we are taking advantage of the fact that branchPrefixes are the same as directory prefixes for local # top-level dirs. prefix = git.branchPrefix(sub) if sub != prefix: toplevelDirs[prefix] = [] toplevelActiveDirs[prefix] = [] for sub in allSubprojects: prefix = git.branchPrefix(sub) if sub != prefix: toplevelDirs[prefix].append(sub) else: toplevelSubs.append(sub) for sub in activeSubprojects: prefix = git.branchPrefix(sub) if sub != prefix: toplevelActiveDirs[prefix].append(sub) included = {} for directory, subprojects in toplevelDirs.items(): activeDir = toplevelActiveDirs[directory] if len(activeDir) == 0: defaultValue = "none" elif set(activeDir) == set(subprojects): defaultValue = "all" else: defaultValue = "some" opt = utility.userInput( "Would you like all, some, or none of the %ss in %s?" % (projectType, directory), default=defaultValue) if opt.lower()[0] == "a": for subproject in subprojects: included[subproject] = True if opt.lower()[0] == "n": for subproject in subprojects: included[subproject] = False if opt.lower()[0] == "s": for subproject in subprojects: included[subproject] = utility.userInput( "Would you like %s %s? [y/n]" % (projectType, subproject), 'y' if (subproject in activeSubprojects) else 'n') for subproject in toplevelSubs: included[subproject] = utility.userInput( "Would you like %s %s? [y/n]" % (projectType, subproject), 'y' if (subproject in activeSubprojects) else 'n') return included
def execute(self, args): sync = args["--sync"].lower().strip() sync = sync == "true" or sync == "yes" args["--sync"] = sync config = grapeConfig.grapeConfig() origwd = os.getcwd() wsDir = utility.workspaceDir() os.chdir(wsDir) base = git.baseDir() if base == "": return False hasSubmodules = len(git.getAllSubmodules()) > 0 and not args["--skipSubmodules"] includedSubmodules = {} includedNestedSubprojectPrefixes = {} allSubmodules = git.getAllSubmodules() allNestedSubprojects = config.getAllNestedSubprojects() addedSubmodules = [] addedNestedSubprojects = [] addedProjects = args["--add"] notFound = [] for proj in addedProjects: if proj in allSubmodules: addedSubmodules.append(proj) elif proj in allNestedSubprojects: addedNestedSubprojects.append(proj) else: notFound.append(proj) rmSubmodules = [] rmNestedSubprojects = [] rmProjects = args["--rm"] for proj in rmProjects: if proj in allSubmodules: rmSubmodules.append(proj) elif proj in allNestedSubprojects: rmNestedSubprojects.append(proj) else: notFound.append(proj) if notFound: utility.printMsg("\"%s\" not found in submodules %s \nor\n nested subprojects %s" % (",".join(notFound),",".join(allSubmodules),",".join(allNestedSubprojects))) return False if not args["--checkSubprojects"]: # get submodules to update if hasSubmodules: if args["--allSubmodules"]: includedSubmodules = {sub:True for sub in allSubmodules} elif args["--add"] or args["--rm"]: includedSubmodules = {sub:True for sub in git.getActiveSubmodules()} includedSubmodules.update({sub:True for sub in addedSubmodules}) includedSubmodules.update({sub:False for sub in rmSubmodules}) else: includedSubmodules = self.defineActiveSubmodules() # get subprojects to update if not args["--skipNestedSubprojects"]: nestedPrefixLookup = lambda x : config.get("nested-%s" % x, "prefix") if args["--allNestedSubprojects"]: includedNestedSubprojectPrefixes = {nestedPrefixLookup(sub):True for sub in allNestedSubprojects} elif args["--add"] or args["--rm"]: includedNestedSubprojectPrefixes = {sub:True for sub in grapeConfig.GrapeConfigParser.getAllActiveNestedSubprojectPrefixes()} includedNestedSubprojectPrefixes.update({nestedPrefixLookup(sub):True for sub in addedNestedSubprojects}) includedNestedSubprojectPrefixes.update({nestedPrefixLookup(sub):False for sub in rmNestedSubprojects}) else: includedNestedSubprojectPrefixes = self.defineActiveNestedSubprojects() if hasSubmodules: initStr = "" deinitStr = "" rmCachedStr = "" resetStr = "" for submodule, nowActive in includedSubmodules.items(): if nowActive: initStr += ' %s' % submodule else: deinitStr += ' %s' % submodule rmCachedStr += ' %s' % submodule resetStr += ' %s' % submodule if args["-f"] and deinitStr: deinitStr = "-f"+deinitStr utility.printMsg("Configuring submodules...") utility.printMsg("Initializing submodules...") git.submodule("init %s" % initStr.strip()) if deinitStr: utility.printMsg("Deiniting submodules that were not requested... (%s)" % deinitStr) done = False while not done: try: git.submodule("deinit %s" % deinitStr.strip()) done = True except git.GrapeGitError as e: if "the following file has local modifications" in e.gitOutput: print e.gitOutput utility.printMsg("A submodule that you wanted to remove has local modifications. " "Use grape uv -f to force removal.") return False elif "use 'rm -rf' if you really want to remove it including all of its history" in e.gitOutput: if not args["-f"]: raise e # it is safe to move the .git of the submodule to the .git/modules area of the workspace... module = None for l in e.gitOutput.split('\n'): if "Submodule work tree" in l and "contains a .git directory" in l: module = l.split("'")[1] break if module: src = os.path.join(module, ".git") dest = os.path.join(wsDir, ".git", "modules", module) utility.printMsg("Moving %s to %s"%(src, dest)) shutil.move(src, dest ) else: raise e else: raise e git.rm("--cached %s" % rmCachedStr) git.reset(" %s" % resetStr) if initStr: utility.printMsg("Updating active submodules...(%s)" % initStr) git.submodule("update") # handle nested subprojects if not args["--skipNestedSubprojects"]: reverseLookupByPrefix = {nestedPrefixLookup(sub) : sub for sub in allNestedSubprojects} userConfig = grapeConfig.grapeUserConfig() updatedActiveList = [] for subproject, nowActive in includedNestedSubprojectPrefixes.items(): subprojectName = reverseLookupByPrefix[subproject] section = "nested-%s" % reverseLookupByPrefix[subproject] userConfig.ensureSection(section) previouslyActive = userConfig.getboolean(section, "active") previouslyActive = previouslyActive and os.path.exists(os.path.join(base, subproject, ".git")) userConfig.set(section, "active", "True" if previouslyActive else "False") if nowActive and previouslyActive: updatedActiveList.append(subprojectName) if nowActive and not previouslyActive: utility.printMsg("Activating Nested Subproject %s" % subproject) if not addSubproject.AddSubproject.activateNestedSubproject(subprojectName, userConfig): utility.printMsg("Can't activate %s. Exiting..." % subprojectName) return False updatedActiveList.append(subprojectName) if not nowActive and not previouslyActive: pass if not nowActive and previouslyActive: #remove the subproject subprojectdir = os.path.join(base, utility.makePathPortable(subproject)) proceed = args["-f"] or \ utility.userInput("About to delete all contents in %s. Any uncommitted changes, committed changes " "that have not been pushed, or ignored files will be lost. Proceed?" % subproject, 'n') if proceed: shutil.rmtree(subprojectdir) userConfig.setActiveNestedSubprojects(updatedActiveList) grapeConfig.writeConfig(userConfig, os.path.join(utility.workspaceDir(), ".git", ".grapeuserconfig")) checkoutArgs = "-b" if args["-b"] else "" safeSwitchWorkspaceToBranch( git.currentBranch(), checkoutArgs, sync) os.chdir(origwd) return True
def execute(self, args): sync = args["--sync"].lower().strip() sync = sync == "true" or sync == "yes" args["--sync"] = sync config = grapeConfig.grapeConfig() origwd = os.getcwd() wsDir = utility.workspaceDir() os.chdir(wsDir) base = git.baseDir() if base == "": return False hasSubmodules = len( git.getAllSubmodules()) > 0 and not args["--skipSubmodules"] includedSubmodules = {} includedNestedSubprojectPrefixes = {} allSubmodules = git.getAllSubmodules() allNestedSubprojects = config.getAllNestedSubprojects() addedSubmodules = [] addedNestedSubprojects = [] addedProjects = args["--add"] notFound = [] for proj in addedProjects: if proj in allSubmodules: addedSubmodules.append(proj) elif proj in allNestedSubprojects: addedNestedSubprojects.append(proj) else: notFound.append(proj) rmSubmodules = [] rmNestedSubprojects = [] rmProjects = args["--rm"] for proj in rmProjects: if proj in allSubmodules: rmSubmodules.append(proj) elif proj in allNestedSubprojects: rmNestedSubprojects.append(proj) else: notFound.append(proj) if notFound: utility.printMsg( "\"%s\" not found in submodules %s \nor\n nested subprojects %s" % (",".join(notFound), ",".join(allSubmodules), ",".join(allNestedSubprojects))) return False if not args["--checkSubprojects"]: # get submodules to update if hasSubmodules: if args["--allSubmodules"]: includedSubmodules = {sub: True for sub in allSubmodules} elif args["--add"] or args["--rm"]: includedSubmodules = { sub: True for sub in git.getActiveSubmodules() } includedSubmodules.update( {sub: True for sub in addedSubmodules}) includedSubmodules.update( {sub: False for sub in rmSubmodules}) else: includedSubmodules = self.defineActiveSubmodules() # get subprojects to update if not args["--skipNestedSubprojects"]: nestedPrefixLookup = lambda x: config.get( "nested-%s" % x, "prefix") if args["--allNestedSubprojects"]: includedNestedSubprojectPrefixes = { nestedPrefixLookup(sub): True for sub in allNestedSubprojects } elif args["--add"] or args["--rm"]: includedNestedSubprojectPrefixes = { sub: True for sub in grapeConfig.GrapeConfigParser. getAllActiveNestedSubprojectPrefixes() } includedNestedSubprojectPrefixes.update({ nestedPrefixLookup(sub): True for sub in addedNestedSubprojects }) includedNestedSubprojectPrefixes.update({ nestedPrefixLookup(sub): False for sub in rmNestedSubprojects }) else: includedNestedSubprojectPrefixes = self.defineActiveNestedSubprojects( ) if hasSubmodules: initStr = "" deinitStr = "" rmCachedStr = "" resetStr = "" for submodule, nowActive in includedSubmodules.items(): if nowActive: initStr += ' %s' % submodule else: deinitStr += ' %s' % submodule rmCachedStr += ' %s' % submodule resetStr += ' %s' % submodule if args["-f"] and deinitStr: deinitStr = "-f" + deinitStr utility.printMsg("Configuring submodules...") utility.printMsg("Initializing submodules...") git.submodule("init %s" % initStr.strip()) if deinitStr: utility.printMsg( "Deiniting submodules that were not requested... (%s)" % deinitStr) done = False while not done: try: git.submodule("deinit %s" % deinitStr.strip()) done = True except git.GrapeGitError as e: if "the following file has local modifications" in e.gitOutput: print e.gitOutput utility.printMsg( "A submodule that you wanted to remove has local modifications. " "Use grape uv -f to force removal.") return False elif "use 'rm -rf' if you really want to remove it including all of its history" in e.gitOutput: if not args["-f"]: raise e # it is safe to move the .git of the submodule to the .git/modules area of the workspace... module = None for l in e.gitOutput.split('\n'): if "Submodule work tree" in l and "contains a .git directory" in l: module = l.split("'")[1] break if module: src = os.path.join(module, ".git") dest = os.path.join( wsDir, ".git", "modules", module) utility.printMsg("Moving %s to %s" % (src, dest)) shutil.move(src, dest) else: raise e else: raise e git.rm("--cached %s" % rmCachedStr) git.reset(" %s" % resetStr) if initStr: utility.printMsg("Updating active submodules...(%s)" % initStr) git.submodule("update") # handle nested subprojects if not args["--skipNestedSubprojects"]: reverseLookupByPrefix = { nestedPrefixLookup(sub): sub for sub in allNestedSubprojects } userConfig = grapeConfig.grapeUserConfig() updatedActiveList = [] for subproject, nowActive in includedNestedSubprojectPrefixes.items( ): subprojectName = reverseLookupByPrefix[subproject] section = "nested-%s" % reverseLookupByPrefix[subproject] userConfig.ensureSection(section) previouslyActive = userConfig.getboolean(section, "active") previouslyActive = previouslyActive and os.path.exists( os.path.join(base, subproject, ".git")) userConfig.set(section, "active", "True" if previouslyActive else "False") if nowActive and previouslyActive: updatedActiveList.append(subprojectName) if nowActive and not previouslyActive: utility.printMsg("Activating Nested Subproject %s" % subproject) if not addSubproject.AddSubproject.activateNestedSubproject( subprojectName, userConfig): utility.printMsg("Can't activate %s. Exiting..." % subprojectName) return False updatedActiveList.append(subprojectName) if not nowActive and not previouslyActive: pass if not nowActive and previouslyActive: #remove the subproject subprojectdir = os.path.join( base, utility.makePathPortable(subproject)) proceed = args["-f"] or \ utility.userInput("About to delete all contents in %s. Any uncommitted changes, committed changes " "that have not been pushed, or ignored files will be lost. Proceed?" % subproject, 'n') if proceed: shutil.rmtree(subprojectdir) userConfig.setActiveNestedSubprojects(updatedActiveList) grapeConfig.writeConfig( userConfig, os.path.join(utility.workspaceDir(), ".git", ".grapeuserconfig")) checkoutArgs = "-b" if args["-b"] else "" safeSwitchWorkspaceToBranch(git.currentBranch(), checkoutArgs, sync) os.chdir(origwd) return True
def execute(self, args): name = args["--name"] prefix = args["--prefix"] url = args["--url"] fullurl = utility.parseSubprojectRemoteURL(url) branch = args["--branch"] config = grapeConfig.grapeConfig() projectType = self.parseSubprojectType(config, args) proceed = args["--noverify"] if projectType == "subtree": # whether or not to squash squash = args["--squash"] or config.get( "subtrees", "mergePolicy").strip().lower() == "squash" squash = squash and not args["--nosquash"] squash_arg = "--squash" if squash else "" # expand the URL if not proceed: proceed = utility.userInput( "About to create a subtree called %s at path %s,\n" "cloned from %s at %s " % (name, prefix, fullurl, branch) + ("using a squash merge." if squash else "") + "\nProceed? [y/n]", "y") if proceed: os.chdir(utility.workspaceDir()) git.subtree("add %s --prefix=%s %s %s" % (squash_arg, prefix, fullurl, branch)) #update the configuration file current_cfg_names = config.get("subtrees", "names").split() if not current_cfg_names or current_cfg_names[0].lower( ) == "none": config.set("subtrees", "names", name) else: current_cfg_names.append(name) config.set("subtrees", "names", ' '.join(current_cfg_names)) section = "subtree-%s" % name config.add_section(section) config.set(section, "prefix", prefix) config.set(section, "remote", url) config.set(section, "topicPrefixMappings", "?:%s" % branch) with open(os.path.join(utility.workspaceDir(), ".grapeconfig"), "w") as f: config.write(f) utility.printMsg( "Successfully added subtree branch. \n" "Updated .grapeconfig file. Review changes and then commit. " ) elif projectType == "submodule": if not proceed: proceed = utility.userInput( "about to add %s as a submodule at path %s,\n" "cloned from %s at branch %s.\nproceed? [y/n]" % (name, prefix, url, branch), "y") if proceed: git.submodule("add --name %s --branch %s %s %s" % (name, branch, url, prefix)) print( "Successfully added submodule %s at %s. Please review changes and commit." % (name, prefix)) elif projectType == "nested": if not proceed: proceed = utility.userInput( " about to clone %s as a nested git repo at path %s,\n" "cloned from %s at branch %s.\nProceed? [y/n]" % (name, prefix, url, branch), 'y') if proceed: git.clone("%s %s" % (fullurl, prefix)) ignorePath = os.path.join(git.baseDir(), ".gitignore") with open(ignorePath, 'a') as ignore: ignore.writelines([prefix + '\n']) git.add(ignorePath) wsConfig = grapeConfig.workspaceConfig() currentSubprojects = wsConfig.getList("nestedProjects", "names") currentSubprojects.append(name) wsConfig.set("nestedProjects", "names", ' '.join(currentSubprojects)) newSection = "nested-%s" % name wsConfig.ensureSection(newSection) wsConfig.set(newSection, "prefix", prefix) wsConfig.set(newSection, "url", url) configFileName = os.path.join(utility.workspaceDir(), ".grapeconfig") with open(os.path.join(configFileName), 'w') as f: wsConfig.write(f) git.add(configFileName) git.commit("%s %s -m \"GRAPE: Added nested subproject %s\"" % (ignorePath, configFileName, prefix)) # update the runtime config with the new workspace .grapeconfig's settings. grapeConfig.read() userConfig = grapeConfig.grapeUserConfig() userConfig.ensureSection(newSection) userConfig.set(newSection, "active", "True") grapeConfig.writeConfig( userConfig, os.path.join(utility.workspaceDir(), ".git", ".grapeuserconfig")) return True
def execute(self, args): base = git.baseDir() if base == "": return False dotGit = git.gitDir() utility.printMsg("Optimizing git performance on slow file systems...") #runs file system intensive tasks such as git status and git commit # in parallel (important for NFS systems such as LC) git.config("core.preloadindex", "true") #have git automatically do some garbage collection / optimization utility.printMsg("Setting up automatic git garbage collection...") git.config("gc.auto", "1") #prevents false conflict detection due to differences in filesystem # time stamps utility.printMsg("Optimizing cross platform portability...") git.config("core.trustctime", "false") # stores login info for 12 hrs (max allowed by RZBitbucket) if not args["--nocredcache"]: cache = args["--credcache"] if not cache: cache = utility.userInput( "Would you like to enable git-managed credential caching?", 'y') if cache: utility.printMsg( "Enabling 12 hr caching of https credentials...") if os.name == "nt": git.config("--global credential.helper", "wincred") else: git.config("--global credential.helper", "cache --timeout=43200") # enables 'as' option for merge strategies -forces a conflict if two branches # modify the same file mergeVerifyPath = os.path.join(os.path.dirname(__file__), "..", "merge-and-verify-driver") if os.path.exists(mergeVerifyPath): utility.printMsg( "Enabling safe merges (triggers conflicts any time same file is modified),\n\t see 'as' option for grape m and grape md..." ) git.config("merge.verify.name", "merge and verify driver") git.config("merge.verify.driver", "%s/merge-and-verify-driver %A %O %B") else: utility.printMsg( "WARNING: merge and verify script not detected, safe merges ('as' option to grape m / md) will not work!" ) # enables lg as an alias to print a pretty-font summary of # key junctions in the history for this branch. utility.printMsg("Setting lg as an alias for a pretty log call...") git.config( "alias.lg", "log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit --date=relative --simplify-by-decoration" ) # perform an update of the active subprojects if asked. ask = not args["--nouv"] updateView = ask and (args["--uv"] or utility.userInput( "Do you want to edit your active subprojects?" " (you can do this later using grape uv) [y/n]", "n")) if updateView: grapeMenu.menu().applyMenuChoice("uv", args["--uvArg"]) # configure git to use p4merge for conflict resolution # and diffing useP4Merge = not args["--nop4merge"] and ( args["--p4merge"] or utility.userInput( "Would you like to use p4merge as your merge tool? [y/n]", "y")) # note that this relies on p4merge being in your path somewhere if (useP4Merge): git.config("merge.keepBackup", "false") git.config("merge.tool", "p4merge") git.config("mergetool.keepBackup", "false") git.config( "mergetool.p4merge.cmd", 'p4merge \"\$BASE\" \"\$LOCAL\" \"\$REMOTE\" \"\$MERGED\"') git.config("mergetool.p4merge.keepTemporaries", "false") git.config("mergetool.p4merge.trustExitCode", "false") git.config("mergetool.p4merge.keepBackup", "false") utility.printMsg( "Configured repo to use p4merge for conflict resolution") else: git.config("merge.tool", "tkdiff") useP4Diff = not args["--nop4diff"] and ( args["--p4diff"] or utility.userInput( "Would you like to use p4merge as your diff tool? [y/n]", "y")) # this relies on p4diff being defined as a custom bash script, with the following one-liner: # [ $# -eq 7 ] && p4merge "$2" "$5" if (useP4Diff): p4diffScript = os.path.join(os.path.dirname(__file__), "..", "p4diff") if os.path.exists(p4diffScript): git.config("diff.external", p4diffScript) utility.printMsg( "Configured repo to use p4merge for diff calls - p4merge must be in your path" ) else: utility.printMsg("Could not find p4diff script at %s" % p4diffScript) useGitP4 = args["--git-p4"] if (useGitP4): git.config("git-p4.useclientspec", "true") # create p4 references to enable imports from p4 p4remotes = os.path.join(dotGit, "refs", "remotes", "p4", "") utility.ensure_dir(p4remotes) commit = utility.userInput( "Please enter a descriptor (e.g. SHA, branch if tip, tag name) of the current git commit that mirrors the p4 repo", "master") sha = git.SHA(commit) with open(os.path.join(p4remotes, "HEAD"), 'w') as f: f.write(sha) with open(os.path.join(p4remotes, "master"), 'w') as f: f.write(sha) # to enable exports to p4, a maindev client needs to be set up haveCopied = False while (not haveCopied): p4settings = utility.userInput( "Enter a path to a .p4settings file describing the maindev client you'd like to use for p4 updates", ".p4settings") try: shutil.copyfile(p4settings, os.path.join(base, ".p4settings")) haveCopied = True except: print( "could not find p4settings file, please check your path and try again" ) return False # install hooks here and in all submodules utility.printMsg("Installing hooks in all repos...") cwd = git.baseDir() grapeMenu.menu().applyMenuChoice("installHooks") # ensure all public branches are available in all repos submodules = git.getActiveSubmodules() config = grapeConfig.grapeConfig() publicBranches = config.getPublicBranchList() submodulePublicBranches = set( config.getMapping('workspace', 'submoduleTopicPrefixMappings').values()) for sub in submodules: self.ensurePublicBranchesExist(grapeConfig.grapeRepoConfig(sub), sub, submodulePublicBranches) # reset config to the workspace grapeconfig, use that one for all nested projects' public branches. wsDir = utility.workspaceDir() config = grapeConfig.grapeRepoConfig(wsDir) for proj in grapeConfig.GrapeConfigParser.getAllActiveNestedSubprojectPrefixes( ): self.ensurePublicBranchesExist(config, os.path.join(wsDir, proj), publicBranches) self.ensurePublicBranchesExist(config, wsDir, publicBranches) return True
def mergeIntoCurrent(self, branchName, args, projectName): choice = False strategy = None if args["--continue"]: if self.continueLocalMerge(args): return True if args['--am']: strategy = 'am' elif args['--as']: strategy = 'as' elif args['--at']: strategy = 'at' elif args['--aT']: strategy = 'aT' elif args['--ay']: strategy = 'ay' elif args['--aY']: strategy = 'aY' if not strategy or args['--askAll']: repoSpec = " in %s" % projectName if args['--askAll'] else "" strategy = utility.userInput( "How do you want to resolve changes%s? [am / as / at / aT / ay / aY] \n" % repoSpec + "am: Auto Merge (default) \n" + "as: Safe Merge - issues conflicts if both branches touch same file.\n" + "at: Accept Theirs - accept changes in %s if both branches touch same file\n" % branchName + "aT: Accept Theirs (if conflicted) - resolves conflicts by accepting changes in %s\n" % branchName + "ay: Accept Yours - accept changes in current branch if both branches touch same file\n" + "aY: Accept Yours (if conflicted) - resolves conflicts by using changes in current branch.", "am") if strategy == 'am': args["--am"] = True utility.printMsg("Merging using git's default strategy...") choice = self.merge(branchName, "", args) elif strategy == 'as' or strategy == 'at' or strategy == 'ay': if strategy == 'as': args["--as"] = True # this employs using the custom low-level merge driver "verify" and # appending a "* merge=verify" to the .gitattributes file. # # see # http://stackoverflow.com/questions/5074452/git-how-to-force-merge-conflict-and-manual-merge-on-selected-file # for details. utility.printMsg( "Merging forcing conflicts whenever both branches edited the same file..." ) elif strategy == 'at': args["--at"] = True elif strategy == 'ay': args["--ay"] = True base = git.gitDir() if base == "": return False attributes = os.path.join(base, ".gitattributes") tmpattributes = None if os.path.exists(attributes): tmpattributes = os.path.join(base, ".gitattributes.tmp") # save original attributes file shutil.copyfile(attributes, tmpattributes) #append merge driver strategy to the attributes file with open(attributes, 'a') as f: f.write("* merge=verify") else: with open(attributes, 'w') as f: f.write("* merge=verify") # perform the merge choice = self.merge(branchName, "", args) # restore original attributes file if tmpattributes: shutil.copyfile(tmpattributes, attributes) os.remove(tmpattributes) else: os.remove(attributes) elif strategy == 'aT': args["--aT"] = True utility.printMsg( "Merging using recursive strategy, resolving conflicts cleanly with changes in %s..." % branchName) choice = self.merge(branchName, "-Xtheirs", args) elif strategy == 'aY': args["--aY"] = True utility.printMsg( "Merging using recursive strategy, resolving conflicts cleanly with current branch's changes..." ) choice = self.merge(branchName, "-Xours", args) return choice
def defineActiveSubmodules(projectType="submodule"): """ Queries the user for the submodules (projectType == "submodule") or nested subprojects (projectType == "nested subproject") they would like to activate. """ if projectType == "submodule": allSubprojects = git.getAllSubmodules() activeSubprojects = git.getActiveSubmodules() if projectType == "nested subproject": config = grapeConfig.grapeConfig() allSubprojectNames = config.getAllNestedSubprojects() allSubprojects = [] for project in allSubprojectNames: allSubprojects.append(config.get("nested-%s" % project, "prefix")) activeSubprojects = grapeConfig.GrapeConfigParser.getAllActiveNestedSubprojectPrefixes() toplevelDirs = {} toplevelActiveDirs = {} toplevelSubs = [] for sub in allSubprojects: # we are taking advantage of the fact that branchPrefixes are the same as directory prefixes for local # top-level dirs. prefix = git.branchPrefix(sub) if sub != prefix: toplevelDirs[prefix] = [] toplevelActiveDirs[prefix] = [] for sub in allSubprojects: prefix = git.branchPrefix(sub) if sub != prefix: toplevelDirs[prefix].append(sub) else: toplevelSubs.append(sub) for sub in activeSubprojects: prefix = git.branchPrefix(sub) if sub != prefix: toplevelActiveDirs[prefix].append(sub) included = {} for directory, subprojects in toplevelDirs.items(): activeDir = toplevelActiveDirs[directory] if len(activeDir) == 0: defaultValue = "none" elif set(activeDir) == set(subprojects): defaultValue = "all" else: defaultValue = "some" opt = utility.userInput("Would you like all, some, or none of the %ss in %s?" % (projectType,directory), default=defaultValue) if opt.lower()[0] == "a": for subproject in subprojects: included[subproject] = True if opt.lower()[0] == "n": for subproject in subprojects: included[subproject] = False if opt.lower()[0] == "s": for subproject in subprojects: included[subproject] = utility.userInput("Would you like %s %s? [y/n]" % (projectType, subproject), 'y' if (subproject in activeSubprojects) else 'n') for subproject in toplevelSubs: included[subproject] = utility.userInput("Would you like %s %s? [y/n]" % (projectType, subproject), 'y' if (subproject in activeSubprojects) else 'n') return included
def handleCheckoutMRE(mre): global _skipBranchCreation global _createNewBranch newBranchReposArgTuples = [] newBranches = [] for e1, branch, project, checkoutargs in zip(mre.exceptions(), mre.branches(), mre.repos(), mre.args()): try: raise e1 except git.GrapeGitError as e: with utility.cd(project): if "pathspec" in e.gitOutput: createNewBranch = _createNewBranch if _skipBranchCreation: utility.printMsg("Skipping checkout of %s in %s" % (branch, project)) createNewBranch = False elif not createNewBranch: createNewBranch = utility.userInput("Branch not found locally or remotely. Would you like to create a " "new branch called %s in %s? \n" "(select 'a' to say yes for (a)ll, 's' to (s)kip creation for branches that don't exist )" "\n(y,n,a,s)" % (branch, project), 'y') if str(createNewBranch).lower()[0] == 'a': _createNewBranch = True createNewBranch = True if str(createNewBranch).lower()[0] == 's': _skipBranchCreation = True createNewBranch = False if createNewBranch: newBranchReposArgTuples.append((project, branch, {"checkout": checkoutargs[0]})) else: continue elif "already exists" in e.gitOutput: utility.printMsg("Branch %s already exists in %s." % (branch, project)) branchDescription = git.commitDescription(branch) headDescription = git.commitDescription("HEAD") if branchDescription == headDescription: utility.printMsg("Branch %s and HEAD are the same. Switching to %s." % (branch, branch)) action = "k" else: utility.printMsg("Branch %s and HEAD are not the same." % branch) action = '' valid = False while not valid: action = utility.userInput("Would you like to \n (k)eep it as is at: %s \n" " or \n (f)orce it to: %s? \n(k,f)" % (branchDescription, headDescription), 'k') valid = (action == 'k') or (action == 'f') if not valid: utility.printMsg("Invalid input. Enter k or f. ") if action == 'k': git.checkout(branch) elif action == 'f': git.checkout("-B %s" % branch) elif "conflict" in e.gitOutput.lower(): utility.printMsg("CONFLICT occurred when pulling %s from origin." % branch) elif "does not appear to be a git repository" in e.gitOutput.lower(): utility.printMsg("Remote 'origin' does not exist. " "This branch was not updated from a remote repository.") elif "Couldn't find remote ref" in e.gitOutput: utility.printMsg("Remote of %s does not have reference to %s. You may want to push this branch. " %(project, branch)) else: raise e if len(newBranchReposArgTuples) > 0: utility.MultiRepoCommandLauncher(createNewBranches, listOfRepoBranchArgTuples=newBranchReposArgTuples).launchFromWorkspaceDir(handleMRE=createNewBranchesMREHandler)
def execute(self,args): base = git.baseDir() if base == "": return False dotGit = git.gitDir() utility.printMsg("Optimizing git performance on slow file systems...") #runs file system intensive tasks such as git status and git commit # in parallel (important for NFS systems such as LC) git.config("core.preloadindex","true") #have git automatically do some garbage collection / optimization utility.printMsg("Setting up automatic git garbage collection...") git.config("gc.auto","1") #prevents false conflict detection due to differences in filesystem # time stamps utility.printMsg("Optimizing cross platform portability...") git.config("core.trustctime","false") # stores login info for 12 hrs (max allowed by RZBitbucket) if not args["--nocredcache"]: cache = args["--credcache"] if not cache: cache = utility.userInput("Would you like to enable git-managed credential caching?", 'y') if cache: utility.printMsg("Enabling 12 hr caching of https credentials...") if os.name == "nt": git.config("--global credential.helper", "wincred") else : git.config("--global credential.helper", "cache --timeout=43200") # enables 'as' option for merge strategies -forces a conflict if two branches # modify the same file mergeVerifyPath = os.path.join(os.path.dirname(__file__),"..","merge-and-verify-driver") if os.path.exists(mergeVerifyPath): utility.printMsg("Enabling safe merges (triggers conflicts any time same file is modified),\n\t see 'as' option for grape m and grape md...") git.config("merge.verify.name","merge and verify driver") git.config("merge.verify.driver","%s/merge-and-verify-driver %A %O %B") else: utility.printMsg("WARNING: merge and verify script not detected, safe merges ('as' option to grape m / md) will not work!") # enables lg as an alias to print a pretty-font summary of # key junctions in the history for this branch. utility.printMsg("Setting lg as an alias for a pretty log call...") git.config("alias.lg","log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit --date=relative --simplify-by-decoration") # perform an update of the active subprojects if asked. ask = not args["--nouv"] updateView = ask and (args["--uv"] or utility.userInput("Do you want to edit your active subprojects?" " (you can do this later using grape uv) [y/n]", "n")) if updateView: grapeMenu.menu().applyMenuChoice("uv", args["--uvArg"]) # configure git to use p4merge for conflict resolution # and diffing useP4Merge = not args["--nop4merge"] and (args["--p4merge"] or utility.userInput("Would you like to use p4merge as your merge tool? [y/n]","y")) # note that this relies on p4merge being in your path somewhere if (useP4Merge): git.config("merge.keepBackup","false") git.config("merge.tool","p4merge") git.config("mergetool.keepBackup","false") git.config("mergetool.p4merge.cmd",'p4merge \"\$BASE\" \"\$LOCAL\" \"\$REMOTE\" \"\$MERGED\"') git.config("mergetool.p4merge.keepTemporaries","false") git.config("mergetool.p4merge.trustExitCode","false") git.config("mergetool.p4merge.keepBackup","false") utility.printMsg("Configured repo to use p4merge for conflict resolution") else: git.config("merge.tool","tkdiff") useP4Diff = not args["--nop4diff"] and (args["--p4diff"] or utility.userInput("Would you like to use p4merge as your diff tool? [y/n]","y")) # this relies on p4diff being defined as a custom bash script, with the following one-liner: # [ $# -eq 7 ] && p4merge "$2" "$5" if (useP4Diff): p4diffScript = os.path.join(os.path.dirname(__file__),"..","p4diff") if os.path.exists(p4diffScript): git.config("diff.external",p4diffScript) utility.printMsg("Configured repo to use p4merge for diff calls - p4merge must be in your path") else: utility.printMsg("Could not find p4diff script at %s" % p4diffScript) useGitP4 = args["--git-p4"] if (useGitP4 ): git.config("git-p4.useclientspec","true") # create p4 references to enable imports from p4 p4remotes = os.path.join(dotGit,"refs","remotes","p4","") utility.ensure_dir(p4remotes) commit = utility.userInput("Please enter a descriptor (e.g. SHA, branch if tip, tag name) of the current git commit that mirrors the p4 repo","master") sha = git.SHA(commit) with open(os.path.join(p4remotes,"HEAD"),'w') as f: f.write(sha) with open(os.path.join(p4remotes,"master"),'w') as f: f.write(sha) # to enable exports to p4, a maindev client needs to be set up haveCopied = False while (not haveCopied): p4settings = utility.userInput("Enter a path to a .p4settings file describing the maindev client you'd like to use for p4 updates",".p4settings") try: shutil.copyfile(p4settings,os.path.join(base,".p4settings")) haveCopied = True except: print("could not find p4settings file, please check your path and try again") return False # install hooks here and in all submodules utility.printMsg("Installing hooks in all repos...") cwd = git.baseDir() grapeMenu.menu().applyMenuChoice("installHooks") # ensure all public branches are available in all repos submodules = git.getActiveSubmodules() config = grapeConfig.grapeConfig() publicBranches = config.getPublicBranchList() submodulePublicBranches = set(config.getMapping('workspace', 'submoduleTopicPrefixMappings').values()) for sub in submodules: self.ensurePublicBranchesExist(grapeConfig.grapeRepoConfig(sub),sub, submodulePublicBranches) # reset config to the workspace grapeconfig, use that one for all nested projects' public branches. wsDir = utility.workspaceDir() config = grapeConfig.grapeRepoConfig(wsDir) for proj in grapeConfig.GrapeConfigParser.getAllActiveNestedSubprojectPrefixes(): self.ensurePublicBranchesExist(config, os.path.join(wsDir,proj), publicBranches) self.ensurePublicBranchesExist(config, wsDir, publicBranches) return True