Why getValues and not copyTo
Range.copyTo is the obvious candidate, and it works — until it doesn't. It copies the cell's formatting, conditional-format rules, data-validation constraints, and any merged-cell geometry alongside the values. If the target sheet has a different column count, a different background color scheme, or a header row with its own formatting, copyTo contaminates it. Every time I have watched this bite people, the symptom is a growing blob of mismatched formatting that's annoying to undo and almost impossible to trace to the original copy operation.
The alternative is to call getRange(row, 1, 1, lastCol).getValues(), which returns a plain 2D array of the cell values, nothing else. Dates come back as JavaScript Date objects, numbers as numbers, empty cells as empty strings. You then pass that array's first (and only) row to appendRow on the destination sheet. No formatting, no formulas, no validation rules transfer. The destination sheet keeps its own formatting intact.
The [0] at the end of getValues() unwraps the outer array. getValues always returns a 2D array (rows x cols); since we're reading exactly one row, we get [[val, val, val]]. Grabbing [0] gives us [val, val, val], which is what appendRow expects.