Implement --newrepo flag.

The --newrepo flag tells emerge to recompile a package if it is now
being pulled from a different repository.

BUG=chromium:200417
TEST=Verify ebuilds get pulled in when repo changes.
TEST=New test cases.
Change-Id: I8d5ff1cfc8c1d23e00c733b861547d3626bc2ece
diff --git a/man/emerge.1 b/man/emerge.1
index 7b507fe..eacb77a 100644
--- a/man/emerge.1
+++ b/man/emerge.1
@@ -553,6 +553,11 @@
 The \fIEMERGE_DEFAULT_OPTS\fR variable may be used to disable this
 option by default.
 .TP
+.BR "\-\-newrepo "
+Tells emerge to recompile a package if it is now being pulled from a
+different repository. This option also implies the
+\fB\-\-selective\fR option.
+.TP
 .BR "\-\-newuse " (\fB\-N\fR)
 Tells emerge to include installed packages where USE
 flags have changed since compilation. This option
diff --git a/pym/_emerge/create_depgraph_params.py b/pym/_emerge/create_depgraph_params.py
index 98a7646..f9accf0 100644
--- a/pym/_emerge/create_depgraph_params.py
+++ b/pym/_emerge/create_depgraph_params.py
@@ -46,6 +46,7 @@
 		myparams['rebuild_if_new_slot'] = rebuild_if_new_slot
 
 	if "--update" in myopts or \
+		"--newrepo" in myopts or \
 		"--newuse" in myopts or \
 		"--reinstall" in myopts or \
 		"--noreplace" in myopts or \
diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py
index 9698607..835bd6b 100644
--- a/pym/_emerge/depgraph.py
+++ b/pym/_emerge/depgraph.py
@@ -5396,54 +5396,56 @@
 						break
 					# Compare built package to current config and
 					# reject the built package if necessary.
+					reinstall_use = ("--newuse" in self._frozen_config.myopts or \
+						"--reinstall" in self._frozen_config.myopts)
+					respect_use = self._dynamic_config.myparams.get("binpkg_respect_use") in ("y", "auto")
 					if built and not useoldpkg and \
 						(not installed or matched_packages) and \
 						not (installed and
 						self._frozen_config.excluded_pkgs.findAtomForPackage(pkg,
-						modified_use=self._pkg_use_enabled(pkg))) and \
-						("--newuse" in self._frozen_config.myopts or \
-						"--reinstall" in self._frozen_config.myopts or \
-						(not installed and self._dynamic_config.myparams.get(
-						"binpkg_respect_use") in ("y", "auto"))):
-						iuses = pkg.iuse.all
-						old_use = self._pkg_use_enabled(pkg)
-						if myeb:
-							pkgsettings.setcpv(myeb)
-						else:
-							pkgsettings.setcpv(pkg)
-						now_use = pkgsettings["PORTAGE_USE"].split()
-						forced_flags = set()
-						forced_flags.update(pkgsettings.useforce)
-						forced_flags.update(pkgsettings.usemask)
-						cur_iuse = iuses
-						if myeb and not usepkgonly and not useoldpkg:
-							cur_iuse = myeb.iuse.all
-						reinstall_for_flags = self._reinstall_for_flags(pkg,
-							forced_flags, old_use, iuses, now_use, cur_iuse)
-						if reinstall_for_flags:
-							if not pkg.installed:
-								self._dynamic_config.ignored_binaries.setdefault(pkg, set()).update(reinstall_for_flags)
+						modified_use=self._pkg_use_enabled(pkg))):
+						if myeb and "--newrepo" in self._frozen_config.myopts and myeb.repo != pkg.repo:
 							break
+						elif reinstall_use or (not installed and respect_use):
+							iuses = pkg.iuse.all
+							old_use = self._pkg_use_enabled(pkg)
+							if myeb:
+								pkgsettings.setcpv(myeb)
+							else:
+								pkgsettings.setcpv(pkg)
+							now_use = pkgsettings["PORTAGE_USE"].split()
+							forced_flags = set()
+							forced_flags.update(pkgsettings.useforce)
+							forced_flags.update(pkgsettings.usemask)
+							cur_iuse = iuses
+							if myeb and not usepkgonly and not useoldpkg:
+								cur_iuse = myeb.iuse.all
+							reinstall_for_flags = self._reinstall_for_flags(pkg,
+								forced_flags, old_use, iuses, now_use, cur_iuse)
+							if reinstall_for_flags:
+								if not pkg.installed:
+									self._dynamic_config.ignored_binaries.setdefault(pkg, set()).update(reinstall_for_flags)
+								break
 					# Compare current config to installed package
 					# and do not reinstall if possible.
-					if not installed and not useoldpkg and \
-						("--newuse" in self._frozen_config.myopts or \
-						"--reinstall" in self._frozen_config.myopts) and \
-						cpv in vardb.match(atom):
-						forced_flags = set()
-						forced_flags.update(pkg.use.force)
-						forced_flags.update(pkg.use.mask)
+					if not installed and not useoldpkg and cpv in vardb.match(atom):
 						inst_pkg = vardb.match_pkgs('=' + pkg.cpv)[0]
-						old_use = inst_pkg.use.enabled
-						old_iuse = inst_pkg.iuse.all
-						cur_use = self._pkg_use_enabled(pkg)
-						cur_iuse = pkg.iuse.all
-						reinstall_for_flags = \
-							self._reinstall_for_flags(pkg,
-							forced_flags, old_use, old_iuse,
-							cur_use, cur_iuse)
-						if reinstall_for_flags:
+						if "--newrepo" in self._frozen_config.myopts and pkg.repo != inst_pkg.repo:
 							reinstall = True
+						elif reinstall_use:
+							forced_flags = set()
+							forced_flags.update(pkg.use.force)
+							forced_flags.update(pkg.use.mask)
+							old_use = inst_pkg.use.enabled
+							old_iuse = inst_pkg.iuse.all
+							cur_use = self._pkg_use_enabled(pkg)
+							cur_iuse = pkg.iuse.all
+							reinstall_for_flags = \
+								self._reinstall_for_flags(pkg,
+								forced_flags, old_use, old_iuse,
+								cur_use, cur_iuse)
+							if reinstall_for_flags:
+								reinstall = True
 					if reinstall_atoms.findAtomForPackage(pkg, \
 							modified_use=self._pkg_use_enabled(pkg)):
 						reinstall = True
diff --git a/pym/_emerge/help.py b/pym/_emerge/help.py
index 52cfd00..ab4e88f 100644
--- a/pym/_emerge/help.py
+++ b/pym/_emerge/help.py
@@ -17,7 +17,7 @@
 	print("          [ " + green("--color")+" < " + turquoise("y") + " | "+ turquoise("n")+" >            ] [ "+green("--columns")+"    ]")
 	print("          [ "+green("--complete-graph")+"             ] [ "+green("--deep")+"       ]")
 	print("          [ "+green("--jobs") + " " + turquoise("JOBS")+" ] [ "+green("--keep-going")+" ] [ " + green("--load-average")+" " + turquoise("LOAD") + "            ]")
-	print("          [ "+green("--newuse")+"    ] [ "+green("--noconfmem")+"  ] [ "+green("--nospinner")+"  ]")
+	print("          [ "+green("--newrepo")+"   ] [ "+green("--newuse")+"     ] [ "+green("--noconfmem")+"  ] [ "+green("--nospinner")+"   ]")
 	print("          [ "+green("--oneshot")+"   ] [ "+green("--onlydeps")+"   ] [ "+ green("--quiet-build")+" [ " + turquoise("y") + " | "+ turquoise("n")+" ]        ]")
 	print("          [ "+green("--reinstall ")+turquoise("changed-use")+"      ] [ " + green("--with-bdeps")+" < " + turquoise("y") + " | "+ turquoise("n")+" >         ]")
 	print(bold("Actions:")+"  [ "+green("--depclean")+" | "+green("--list-sets")+" | "+green("--search")+" | "+green("--sync")+" | "+green("--version")+"        ]")
diff --git a/pym/_emerge/main.py b/pym/_emerge/main.py
index da223a6..6225fc9 100644
--- a/pym/_emerge/main.py
+++ b/pym/_emerge/main.py
@@ -36,6 +36,7 @@
 "--fetchonly",    "--fetch-all-uri",
 "--ignore-default-opts",
 "--noconfmem",
+"--newrepo",
 "--newuse",
 "--nodeps",       "--noreplace",
 "--nospinner",    "--oneshot",
diff --git a/pym/portage/tests/emerge/test_simple.py b/pym/portage/tests/emerge/test_simple.py
index d3bb866..bf0af8b 100644
--- a/pym/portage/tests/emerge/test_simple.py
+++ b/pym/portage/tests/emerge/test_simple.py
@@ -247,6 +247,7 @@
 			ebuild_cmd + (test_ebuild, "manifest", "clean", "package", "merge"),
 			emerge_cmd + ("--pretend", "--tree", "--complete-graph", "dev-libs/A"),
 			emerge_cmd + ("-p", "dev-libs/B"),
+			emerge_cmd + ("-p", "--newrepo", "dev-libs/B"),
 			emerge_cmd + ("-B", "dev-libs/B",),
 			emerge_cmd + ("--oneshot", "--usepkg", "dev-libs/B",),
 
diff --git a/pym/portage/tests/resolver/test_multirepo.py b/pym/portage/tests/resolver/test_multirepo.py
index 8d65b2c..5c3ef7e 100644
--- a/pym/portage/tests/resolver/test_multirepo.py
+++ b/pym/portage/tests/resolver/test_multirepo.py
@@ -37,11 +37,20 @@
 
 			"dev-libs/I-1::repo2": { "SLOT" : "1"},
 			"dev-libs/I-2::repo2": { "SLOT" : "2"},
+
+			"dev-libs/K-1::repo2": { },
 			}
 
 		installed = {
 			"dev-libs/H-1": { "RDEPEND" : "|| ( dev-libs/I:2 dev-libs/I:1 )"},
 			"dev-libs/I-2::repo1": {"SLOT" : "2"},
+			"dev-libs/K-1::repo1": { },
+			}
+
+		binpkgs = {
+			"dev-libs/C-1::repo2": { },
+			"dev-libs/I-2::repo1": {"SLOT" : "2"},
+			"dev-libs/K-1::repo2": { },
 			}
 
 		sets = {
@@ -96,6 +105,68 @@
 				check_repo_names = True,
 				mergelist = ["dev-libs/D-1::repo2"]),
 
+			#--usepkg: don't reinstall on new repo without --newrepo
+			ResolverPlaygroundTestCase(
+				["dev-libs/C"],
+				options = {"--usepkg": True, "--selective": True},
+				success = True,
+				check_repo_names = True,
+				mergelist = ["[binary]dev-libs/C-1::repo2"]),
+
+			#--usepkgonly: don't reinstall on new repo without --newrepo
+			ResolverPlaygroundTestCase(
+				["dev-libs/C"],
+				options = {"--usepkgonly": True, "--selective": True},
+				success = True,
+				check_repo_names = True,
+				mergelist = ["[binary]dev-libs/C-1::repo2"]),
+
+			#--newrepo: pick ebuild if binpkg/ebuild have different repo
+			ResolverPlaygroundTestCase(
+				["dev-libs/C"],
+				options = {"--usepkg": True, "--newrepo": True, "--selective": True},
+				success = True,
+				check_repo_names = True,
+				mergelist = ["dev-libs/C-1::repo1"]),
+
+			#--newrepo --usepkgonly: ebuild is ignored
+			ResolverPlaygroundTestCase(
+				["dev-libs/C"],
+				options = {"--usepkgonly": True, "--newrepo": True, "--selective": True},
+				success = True,
+				check_repo_names = True,
+				mergelist = ["[binary]dev-libs/C-1::repo2"]),
+
+			#--newrepo: pick ebuild if binpkg/ebuild have different repo
+			ResolverPlaygroundTestCase(
+				["dev-libs/I"],
+				options = {"--usepkg": True, "--newrepo": True, "--selective": True},
+				success = True,
+				check_repo_names = True,
+				mergelist = ["dev-libs/I-2::repo2"]),
+
+			#--newrepo --usepkgonly: if binpkg matches installed, do nothing
+			ResolverPlaygroundTestCase(
+				["dev-libs/I"],
+				options = {"--usepkgonly": True, "--newrepo": True, "--selective": True},
+				success = True,
+				mergelist = []),
+
+			#--newrepo --usepkgonly: reinstall if binpkg has new repo.
+			ResolverPlaygroundTestCase(
+				["dev-libs/K"],
+				options = {"--usepkgonly": True, "--newrepo": True, "--selective": True},
+				success = True,
+				check_repo_names = True,
+				mergelist = ["[binary]dev-libs/K-1::repo2"]),
+
+			#--usepkgonly: don't reinstall on new repo without --newrepo.
+			ResolverPlaygroundTestCase(
+				["dev-libs/K"],
+				options = {"--usepkgonly": True, "--selective": True},
+				success = True,
+				mergelist = []),
+
 			#Atoms with slots
 			ResolverPlaygroundTestCase(
 				["dev-libs/E"],
@@ -137,6 +208,15 @@
 				success = True,
 				mergelist = []),
 
+			# Dependency on installed dev-libs/I-2 ebuild should trigger reinstall
+			# when --newrepo flag is used.
+			ResolverPlaygroundTestCase(
+				["dev-libs/H"],
+				options = {"--update": True, "--deep": True, "--newrepo": True},
+				success = True,
+				check_repo_names = True,
+				mergelist = ["dev-libs/I-2::repo2"]),
+
 			# Check interaction between repo priority and unsatisfied
 			# REQUIRED_USE, for bug #350254.
 			ResolverPlaygroundTestCase(
@@ -147,7 +227,7 @@
 			)
 
 		playground = ResolverPlayground(ebuilds=ebuilds,
-			installed=installed, sets=sets)
+			binpkgs=binpkgs, installed=installed, sets=sets)
 		try:
 			for test_case in test_cases:
 				playground.run_TestCase(test_case)