Remove accidental duplicates in PK table, cascade changes to FK table
This solution requires first that there aren't any conflicting or cascading information in plant_name_mapping; to work, the following query must return no records:

select *
from plant_name_mapping o
join plant_name_mapping n ON ((n.newgenus, n.newspecies) = (o.oldgenus, o.oldspecies));


The queries to update your tables are:

create temp table plantschanged as
with
  renamed as (
    select o.*, n.newgenus, n.newspecies, n.newspecies is not null as namechanged
    from plants o
    left join plant_name_mapping n on ((n.oldgenus, n.oldspecies) = (o.genus, o.species))
      ),
  newplants as (
    select id, coalesce(newgenus, genus) as genus, coalesce(newspecies, species) as species, namechanged
    from renamed
    ),
  plantsnewid as (
    select *, min(id) over(partition by genus, species) as new_id
    from newplants
    )
select *
from plantsnewid
where namechanged or new_id &lt;&gt; id;

with
  plantsrenamed as (
    update plants p set genus = pc.genus, species = pc.species
    from plantschanged pc
    where p.id = pc.id and pc.id = pc.new_id
    returning p.id
    )
update samples s set tree = pc.new_id
from plantschanged pc
where s.tree = pc.id and pc.id not in (select * from plantsrenamed);

delete from plants
where id in (select id from plantschanged where new_id &lt;&gt; id);


The table plants may have more dependencies than just the table samples. You may check through the following query if you have any further tables that would require updating (perform the same query as update samples with each of these, prior to delete from plants):

select conrelid::regclass, conname
from pg_constraint
where confrelid = 'plants'::regclass and contype = 'f';