My answer #2: Not relying on the orientation of the 2 groups.
Let's again say we want to rotate v1 and v1n to v2 and v2n. We can use .setFromUnitVectors(v1, v2) to get quaternion Q1 rotating v1 to v2. v1n will rotate into v1nrot1, which is normal to v2, but is not v2n. We can use .setFromUnitVectors(v1nrot1, v2n) again to get quaternion Q2 rotating v1nrot1 to v2n. This 2nd rotation will not affect v1rot (=v2) because v1rot is normal to v1nrot1 and v2n. If these were not normal, there would be an extra step to get normals. Finally, v1rot dot v2 = 1 and v1nrot2 dot v2n = 1 showing the Q2Q1 rotation worked. The code is commented.
<!doctype html>
<html>
<head>
<title> SO.html </title>
<style type="text/css">
* { font-family:monospace; font-size:16px; }
</style>
<script src="http://threejs.org/build/three.js"></script>
<script type="text/javascript">
"use strict";
const k57=180/Math.PI;
var div1elt;
window.onload = function() {
div1elt = document.getElementById("div1");
print(" -- v1 and v1n");
var v1 = new THREE.Vector3(1,2,3).normalize();
var vtmp = new THREE.Quaternion(1,0,0).normalize();
var v1n = v1.clone().cross(vtmp).normalize();
printv("v1", v1);
printv("v1n", v1n);
print("v1 dot v1n", v1.dot(v1n));
print(" -- v2 and v2n");
var v2 = new THREE.Vector3(4,3,2).normalize();
var vtmp = new THREE.Quaternion(0,0,1).normalize();
var v2n = v2.clone().cross(vtmp).normalize();
printv("v2", v2);
printv("v2n", v2n);
print("v2 dot v2n", v2.dot(v2n));
var Q1 = new THREE.Quaternion().setFromUnitVectors(v1, v2);
var v1nrot1 = v1n.clone().applyQuaternion(Q1);
var Q2 = new THREE.Quaternion().setFromUnitVectors(v1nrot1, v2n);
var Q12 = new THREE.Quaternion().multiplyQuaternions(Q2,Q1);
printq(" -- Q12 --",Q12);
print(" -- check results");
var v1rot = v1.clone().applyQuaternion(Q12);
print("v1rot dot v2", v1rot.dot(v2).dd(1,5));
var v1nrot2 = v1n.clone().applyQuaternion(Q12);
print("v1nrot2 dot v2n", v1nrot2.dot(v2n).dd(1,5));
};
Number.prototype.dd = function(b4, af) {
if (isNaN(parseFloat(this)) || !isFinite(this)) return this;
var pfx = "", pos, b4s, b4v, afs, afv;
var s = String(this+.5*Math.pow(10,-af));
if (s.substring(0,1) == "-") { pfx = "-"; s = s.substring(1); b4-=1; }
if (s.substring(0,1) == "0") s = s.substring(1);
if ((pos = s.indexOf("."))==-1) s+=".";
b4s = s.substring(0,pos); b4v = b4s.length;
afs = s.substring(pos+1); afv = afs.length;
if (b4>b4v) pfx+= "0".repeat(b4-b4v);
if (af>afv) afs +="0".repeat(af-afv);
if (af<afv) afs = afs.substring(0,af);
return pfx+b4s+((af!=0)?".":"")+afs;
};
function printq(txt, q) {
var ang = Math.atan2(Math.sqrt(q.x*q.x+q.y*q.y+q.z*q.z), q.w);
var len = Math.sqrt(q.w*q.w+q.x*q.x+q.y*q.y+q.z*q.z);
print(txt, q.w.dd(2,5),q.x.dd(2,5),q.y.dd(2,5),q.z.dd(2,5),
"[len="+len.dd(2,5)+", ang="+(ang*k57).dd(4,1)+"]");
}
function printv(txt, v) {
var len = Math.sqrt(v.x*v.x+v.y*v.y+v.z*v.z);
print(txt, v.x.dd(2,5),v.y.dd(2,5),v.z.dd(2,5),
"[len="+len.dd(2,5)+"]");
}
function print(what) {
var s="";
for (var i1=0; i1<arguments.length; i1+=1) {
if (s != "") s += ", ";
s+= arguments[i1];
}
div1elt.innerHTML += s + "<br />";
}
</script>
</head>
<body>
<div id="div1"></div>
</body>
</html>