How to get Quaternion.FromToRotation and hit.normal to allow the Y-axis to not be set to 0

I implemented a terrain follow script that looks like this:

	var fwd:Vector3 = transform.TransformDirection(Vector3.forward);
	var up:Vector3 = transform.TransformDirection(Vector3.up);

	transform.localPosition += fwd/speedModifier;

	var hit:RaycastHit;
	Debug.DrawRay(transform.localPosition, -up*20, Color.cyan);
	if (Physics.Raycast(transform.localPosition, -up, hit, 10)){

		if ( hit.distance > 1 ){
			transform.localPosition.y -= hit.distance-1;
		} else if ( hit.distance < 1 ){
			transform.localPosition.y += 1-hit.distance;
		}

		var rot:Quaternion = Quaternion.FromToRotation(Vector3.up, hit.normal);
		transform.rotation = rot;

	}

The issue I am running into is when setting the rotation of the transform it is always snapping the GO on the Y-axis to 0 instead of using the Y-axis rotation already applied (for example 180). I have tried a bunch of different things which either gave undesirable results or setting the Y-axis to 0.

Essentially, how can I get the hit.normal and apply it to the GO transform via Quaternion.FromToRotation and it not snap the Y of the transform?

Thanks in advance!

Let's try it again, now read what you want, function below should do the trick. What it does is calculating a projection of the GO's forward vector on plane defined by normal vector of hit, then generate a rotation that looks in direction of the projection vector and has the normal vector as up vector.

var speed = 0.5;

function Update () {
    var fwd:Vector3 = transform.forward;

    transform.position += fwd * speed * Time.deltaTime;

    var hit:RaycastHit;
    // instead of -Vector3.up you could use -transform.up but as hit point will jump
    // when slope changes it will give jitter. That's solvable as well by working from
    // a pivot point in bottom centre of object instead of centre (and to make sure
    // your raycast won't be too low move start pos back by a bit using again
    // transform.up as direction.
    if (Physics.Raycast(transform.position, -Vector3.up, hit, 10)){

    	if ( hit.distance > 1 ){
    			transform.localPosition.y -= hit.distance-1;
    	} else if ( hit.distance < 1 ){
    			transform.localPosition.y += 1-hit.distance;
    	}

    	var proj : Vector3 = fwd - (Vector3.Dot(fwd, hit.normal)) * hit.normal;
    	transform.rotation = Quaternion.LookRotation(proj, hit.normal);
    }
}

Adding to Jaap’s Answer. I had the same problem with the y-rotation snapping when i tried to align something with the terrain.

        public static void AlignTransform(Transform transform)
        {
            Vector3 sample = SampleNormal(transform.position);
           
            Vector3 proj = transform.forward - (Vector3.Dot(transform.forward, sample)) * sample;
            transform.rotation = Quaternion.LookRotation(proj, sample);
        }

        public static Vector3 SampleNormal(Vector3 position)
        {
            Terrain terrain = Terrain.activeTerrain;
            var terrainLocalPos = position - terrain.transform.position;
            var normalizedPos = new Vector2(
                Mathf.InverseLerp(0f, terrain.terrainData.size.x, terrainLocalPos.x),
                Mathf.InverseLerp(0f, terrain.terrainData.size.z, terrainLocalPos.z)
            );
            var terrainNormal = terrain.terrainData.GetInterpolatedNormal(normalizedPos.x, normalizedPos.y);

            return terrainNormal;
        }

This does not use a raycast to get the normal. and instead gets the normal from the terrain data.

Thank you so much @Weblyan. I spent hours looking for a way to do that.