diff --git a/test/python_utils/gitdist_UnitTests.py b/test/python_utils/gitdist_UnitTests.py index e4d55357f..b509e9df4 100644 --- a/test/python_utils/gitdist_UnitTests.py +++ b/test/python_utils/gitdist_UnitTests.py @@ -620,6 +620,8 @@ def assertContainsAllGitdistHelpSections(testObj, cmndOut): GeneralScriptSupport.extractLinesMatchingRegex(cmndOut,"^USAGE TIPS:$"), "USAGE TIPS:\n") testObj.assertEqual( GeneralScriptSupport.extractLinesMatchingRegex(cmndOut,"^SCRIPT DEPENDENCIES:$"), "SCRIPT DEPENDENCIES:\n") + testObj.assertEqual( + GeneralScriptSupport.extractLinesMatchingRegex(cmndOut,"^DEFAULT BRANCH SPECIFICATION:$"), "DEFAULT BRANCH SPECIFICATION:\n") class test_gitdist(unittest.TestCase): @@ -676,7 +678,7 @@ def test_dist_help_all_help(self): # Tet that --dist-help --help prints nice error message def test_dist_help_help(self): cmndOut = getCmndOutput(gitdistPath+" --dist-help --help") - cmndOut_expected = "gitdist: error: option --dist-help: invalid choice: '--help' (choose from '', 'overview', 'repo-selection-and-setup', 'dist-repo-status', 'repo-versions', 'aliases', 'usage-tips', 'script-dependencies', 'all')\n" + cmndOut_expected = "gitdist: error: option --dist-help: invalid choice: '--help' (choose from '', 'overview', 'repo-selection-and-setup', 'dist-repo-status', 'repo-versions', 'aliases', 'default-branch', 'usage-tips', 'script-dependencies', 'all')\n" self.assertEqual(s(cmndOut), s(cmndOut_expected)) @@ -684,7 +686,7 @@ def test_dist_help_help(self): def test_dist_help_invalid_pick_help(self): cmndOut = getCmndOutput(gitdistPath+" --dist-help=invalid-pick --help") assertContainsGitdistHelpHeader(self, cmndOut) - errorToFind = "gitdist: error: option --dist-help: invalid choice: 'invalid-pick' (choose from '', 'overview', 'repo-selection-and-setup', 'dist-repo-status', 'repo-versions', 'aliases', 'usage-tips', 'script-dependencies', 'all')" + errorToFind = "gitdist: error: option --dist-help: invalid choice: 'invalid-pick' (choose from '', 'overview', 'repo-selection-and-setup', 'dist-repo-status', 'repo-versions', 'aliases', 'default-branch', 'usage-tips', 'script-dependencies', 'all')" self.assertEqual( GeneralScriptSupport.extractLinesMatchingSubstr(cmndOut,errorToFind), errorToFind+"\n") @@ -1400,5 +1402,74 @@ def test_dist_repo_status_extra_args_fail(self): os.chdir(testBaseDir) + def test_dist_default_branch(self): + os.chdir(testBaseDir) + try: + + # Create a mock git meta-project + + testDir = createAndMoveIntoTestDir("gitdist_default_branch") + os.mkdir("ExtraRepo1") + os.makedirs("Path/To/ExtraRepo2") + os.mkdir("ExtraRepo3") + + # Make sure .gitdist.default is found and read correctly + open(".gitdist.default", "w").write( + ". master\n" \ + "ExtraRepo1 develop\n" \ + "Path/To/ExtraRepo2 app-devel\n" \ + "MissingExtraRepo\n" \ + "ExtraRepo3\n" + ) + cmndOut = GeneralScriptSupport.getCmndOutput( + gitdistPathMock+" checkout _DEFAULT_BRANCH_", workingDir=testDir) + cmndOut_expected = \ + "\n*** Base Git Repo: MockProjectDir\n" \ + "['mockgit', 'checkout', 'master']\n\n" \ + "*** Git Repo: ExtraRepo1\n" \ + "['mockgit', 'checkout', 'develop']\n\n" \ + "*** Git Repo: Path/To/ExtraRepo2\n" \ + "['mockgit', 'checkout', 'app-devel']\n\n" \ + "*** Git Repo: ExtraRepo3\n" \ + "['mockgit', 'checkout', 'master']\n\n" + self.assertEqual(s(cmndOut), s(cmndOut_expected)) + # NOTE: Above ensures that all of the paths are read correctly and that + # missing paths (MissingExtraRepo) are ignored. + + # Make sure that .gitdist overrides .gitdist.default + open(".gitdist", "w").write( + ". develop\n" \ + "ExtraRepo1 develop\n" \ + "ExtraRepo3 develop\n" + ) + cmndOut = GeneralScriptSupport.getCmndOutput( + gitdistPathMock+" checkout _DEFAULT_BRANCH_", workingDir=testDir) + cmndOut_expected = \ + "\n*** Base Git Repo: MockProjectDir\n" \ + "['mockgit', 'checkout', 'develop']\n\n" \ + "*** Git Repo: ExtraRepo1\n" \ + "['mockgit', 'checkout', 'develop']\n\n" \ + "*** Git Repo: ExtraRepo3\n" \ + "['mockgit', 'checkout', 'develop']\n\n" + self.assertEqual(s(cmndOut), s(cmndOut_expected)) + + # Make sure that --dist-repos overrides all files + cmndOut = GeneralScriptSupport.getCmndOutput( + gitdistPathMock+" --dist-repos=.,ExtraRepo1,Path/To/ExtraRepo2 "+ \ + "checkout _DEFAULT_BRANCH_", + workingDir=testDir) + cmndOut_expected = \ + "\n*** Base Git Repo: MockProjectDir\n" \ + "['mockgit', 'checkout', 'master']\n\n" \ + "*** Git Repo: ExtraRepo1\n" \ + "['mockgit', 'checkout', 'master']\n\n" \ + "*** Git Repo: Path/To/ExtraRepo2\n" \ + "['mockgit', 'checkout', 'master']\n\n" + self.assertEqual(s(cmndOut), s(cmndOut_expected)) + + finally: + os.chdir(testBaseDir) + + if __name__ == '__main__': unittest.main() diff --git a/tribits/doc/developers_guide/TribitsDevelopersGuide.rst b/tribits/doc/developers_guide/TribitsDevelopersGuide.rst index ddf7117fc..e702e1b68 100644 --- a/tribits/doc/developers_guide/TribitsDevelopersGuide.rst +++ b/tribits/doc/developers_guide/TribitsDevelopersGuide.rst @@ -8631,6 +8631,7 @@ from `gitdist --help`_ and ``gitdist --dist-help=``: * `gitdist --dist-help=dist-repo-status`_ * `gitdist --dist-help=repo-versions`_ * `gitdist --dist-help=aliases`_ +* `gitdist --dist-help=default-branch`_ * `gitdist --dist-help=usage-tips`_ * `gitdist --dist-help=script-dependencies`_ * `gitdist --dist-help=all`_ @@ -8675,6 +8676,12 @@ gitdist --dist-help=aliases .. include:: gitdist-dist-help-aliases.txt :literal: +gitdist --dist-help=default-branch +++++++++++++++++++++++++++++++++++ + +.. include:: gitdist-dist-help-default-branch.txt + :literal: + gitdist --dist-help=usage-tips ++++++++++++++++++++++++++++++ diff --git a/tribits/doc/developers_guide/generate-dev-guide.sh b/tribits/doc/developers_guide/generate-dev-guide.sh index 2c5982f9f..f32cc194f 100755 --- a/tribits/doc/developers_guide/generate-dev-guide.sh +++ b/tribits/doc/developers_guide/generate-dev-guide.sh @@ -115,6 +115,7 @@ if [ "$TRIBITS_DEV_GUIDE_SKIP_OTHER_EXTRACTION" == "" ] ; then generate_gitdist_dist_help_topic dist-repo-status generate_gitdist_dist_help_topic repo-versions generate_gitdist_dist_help_topic aliases + generate_gitdist_dist_help_topic default-branch generate_gitdist_dist_help_topic usage-tips generate_gitdist_dist_help_topic script-dependencies generate_gitdist_dist_help_topic all diff --git a/tribits/python_utils/gitdist.py b/tribits/python_utils/gitdist.py index 6d2a36175..ad2a07a62 100755 --- a/tribits/python_utils/gitdist.py +++ b/tribits/python_utils/gitdist.py @@ -49,6 +49,7 @@ def s(x): 'dist-repo-status', 'repo-versions', 'aliases', + 'default-branch', 'usage-tips', 'script-dependencies' ] @@ -468,6 +469,47 @@ def getHelpTopicsStr(): helpTopicsDict.update( { 'aliases' : usefulAliasesHelp } ) +defaultBranchHelp = r""" +DEFAULT BRANCH SPECIFICATION: + +When using any git command that accepts a reference (a SHA1, or branch or tag +name), it is possible to use _DEFAULT_BRANCH_ instead. For instance, + + gitdist checkout _DEFAULT_BRANCH_ + +will check out the default development branch in each repository being managed +by gitdist. You can specify the default branch for each repository in your +.gitdist[.default] file. For instance, if your .gitdist file contains + + . master + extraRepo1 develop + extraRepo2 app-devel + +then the command above would check out 'master' in the base repo, 'develop' in +extraRepo1, and 'app-devel' in extraRepo2. This makes it convenient when +working with multiple repositories that have different names for their main +development branches. For instance, you can do a topic branch workflow like: + + gitdist checkout _DEFAULT_BRANCH_ + gitdist pull + gitdist checkout -b newFeatureBranch + + gitdist fetch + gitdist merge origin/_DEFAULT_BRANCH_ + + gitdist checkout _DEFAULT_BRANCH_ + gitdist pull + gitdist merge newFeatureBranch + +and not worry about this 'newFeatureBranch' being off of 'master' in the root +repo, off of 'develop' in extraRepo1, and off of 'app-devel' in extraRepo2. + +If no branch name is specified for any given repository in the +.gitdist[.default] file, then 'master' is assumed. +""" +helpTopicsDict.update( { 'default-branch' : defaultBranchHelp } ) + + usageTipsHelp = r""" USAGE TIPS: @@ -819,6 +861,22 @@ def addColorToErrorMsg(useColor, strIn): return strIn +# Get the paths to all the repos gitdist will work on, along with any optional +# default branches. +def parseGitdistFile(gitdistfile): + reposFullList = [] + defaultBranchDict = {} + with open(gitdistfile, 'r') as file: + for line in file: + entries = line.split() + reposFullList.append(entries[0]) + if len(entries) > 1: + defaultBranchDict[entries[0]] = entries[1] + else: + defaultBranchDict[entries[0]] = "master" + return (reposFullList, defaultBranchDict) + + # Get the commandline options def getCommandlineOps(): @@ -1075,6 +1133,9 @@ def getCommandlineOps(): if options.repos: reposFullList = options.repos.split(",") + defaultBranchDict = {} + for repo in reposFullList: + defaultBranchDict[repo] = "master" else: if os.path.exists(".gitdist"): gitdistfile = ".gitdist" @@ -1083,9 +1144,10 @@ def getCommandlineOps(): else: gitdistfile = None if gitdistfile: - reposFullList = open(gitdistfile, 'r').read().split() + (reposFullList, defaultBranchDict) = parseGitdistFile(gitdistfile) else: reposFullList = ["."] # The default is the base repo + defaultBranchDict = {".": "master"} # Get list of not extra repos @@ -1098,7 +1160,7 @@ def getCommandlineOps(): # G) Return # - return (options, nativeCmnd, otherArgs, reposFullList, + return (options, nativeCmnd, otherArgs, reposFullList, defaultBranchDict, notReposFullList) @@ -1205,13 +1267,28 @@ def replaceRepoVersionInCmndLineArgs(cmndLineArgsArray, repoDirName, \ return cmndLineArgsArrayRepo +# Replace _DEFAULT_BRANCH_ in the command line arguments with the appropriate +# default branch name. +def replaceDefaultBranchInCmndLineArgs(cmndLineArgsArray, repoDirName, \ + defaultBranchDict \ + ): + cmndLineArgsArrayDefaultBranch = [] + for cmndLineArg in cmndLineArgsArray: + newCmndLineArg = re.sub("_DEFAULT_BRANCH_", \ + defaultBranchDict[repoDirName], cmndLineArg) + cmndLineArgsArrayDefaultBranch.append(newCmndLineArg) + return cmndLineArgsArrayDefaultBranch + + # Generate the command line arguments def runRepoCmnd(options, cmndLineArgsArray, repoDirName, baseDir, \ - repoVersionDict, repoVersionDict2 \ + repoVersionDict, repoVersionDict2, defaultBranchDict \ ): - cmndLineArgsArryRepo = replaceRepoVersionInCmndLineArgs(cmndLineArgsArray, \ + cmndLineArgsArrayRepo = replaceRepoVersionInCmndLineArgs(cmndLineArgsArray, \ repoDirName, repoVersionDict, repoVersionDict2) - egCmndArray = [ options.useGit ] + cmndLineArgsArryRepo + cmndLineArgsArrayDefaultBranch = replaceDefaultBranchInCmndLineArgs( \ + cmndLineArgsArrayRepo, repoDirName, defaultBranchDict) + egCmndArray = [ options.useGit ] + cmndLineArgsArrayDefaultBranch runCmnd(options, egCmndArray) @@ -1423,8 +1500,8 @@ def getRepoName(repoDir, baseRepoName): if __name__ == '__main__': - (options, nativeCmnd, otherArgs, reposFullList, notReposList) = \ - getCommandlineOps() + (options, nativeCmnd, otherArgs, reposFullList, defaultBranchDict, \ + notReposList) = getCommandlineOps() if nativeCmnd == "dist-repo-status": distRepoStatus = True @@ -1496,7 +1573,7 @@ def getRepoName(repoDir, baseRepoName): print("*** Tracking branch for git repo '" + repoName + "' = '" + repoStats.trackingBranch + "'") runRepoCmnd(options, cmndLineArgsArray, repo, baseDir, \ - repoVersionDict, repoVersionDict2) + repoVersionDict, repoVersionDict2, defaultBranchDict) if options.debug: print("*** Changing to directory " + baseDir)