晋军拿下曹国后,稍作休整,全面开进卫国。卫成公躲在襄牛城内,卫国朝中众大夫皆愿听命于晋国。因此,晋军进入卫国,没有遇到任何卫国军队的抵抗,鲁国军队已经撤回国内,在卫国境内只有晋军的大约四万人的军队,还有尚未完成集结的楚军。楚成王出于对晋文公和晋国的了解,对与楚作战比较慎重,楚国大军四万余人还在宋国境内,只有令尹成得臣、将军斗椒率领的一百八十乘楚军已进入卫国境内。
成得臣派大夫宛春前往晋军营中,向晋国提出要求:“请晋侯恢复卫侯的君位并退出对曹国的占领,把土地退还给曹国,楚国方可解除对宋国的包围。”
晋国上军副帅狐偃说:“子玉(成得臣的字)太无礼了!他答应国君的,只是解除对宋国的包围这一项;而要求国君答应的,却是恢复卫侯的国君之位和退出对曹国的占领这两项。机不可失,我们抓住机会干掉他算了!”
中军元帅先轸说:“国君可以答应他!安定别国可谓礼,楚国一句话安定了三国,如果我们拒绝,等于一句话灭亡了三国,那我们晋国成了无礼之国了,还拿什么赢得诸侯的支持呢?我们是为了救援宋国而来,来了又不救援宋国,又是没有信誉,也将失去诸侯的信任。不如我们私下答应恢复卫侯君位、退出对曹国的占领,条件是曹国和卫国与楚国绝交,然后我们扣押楚国使节宛春以激怒楚国,等与楚国交战后再说。”
晋文公采纳了先轸的计策,晋军把楚使宛春扣押在了卫国,同时私下向卫国许诺将恢复卫成公的君位,还私下向曹共公许诺重建曹国并退还曹国的被占土地,条件就是与楚国绝交。卫成公、曹共公都痛快地答应了,他们为能够站在强大的晋国这条船上而感到十分庆幸。于是卫、曹都与楚国断绝来往,唯晋国之命是从。
楚国令尹成得臣大怒,率楚军追击晋军。晋文公命晋国军队后撤,晋军的将官们很纳闷,有人说:“晋军势众,国君避让楚国臣下,真是耻辱!而且楚军已经显出疲惫之态,我们为何要后退呢?”楚国自僖公二十六年秋天出兵伐宋,已有一年半了,且围困宋国近一年的时间,没有攻克宋国都城。从楚成王亲自出马会合陈、蔡、郑等国出兵增援,也有三四个月了,楚军远离南方的故土,在北方的中原战场长期转战,确实已经疲惫不堪。
狐偃对他们说:“出兵作战,理直就是气壮,理曲就是气衰,并不在于是否长期在外作战。当年如果没有楚国的善意和恩惠,国君就回不到晋国,我们今天就到不了这里。当年国君向楚王承诺退避三舍,作为对楚国的报答。背弃恩惠,失去信用,我们就理曲、楚军就理直,楚军士气一向饱满,不能认为他们已经疲惫。我们退,如果楚军也退,那当然可以;如果我们退,他们不回撤,那就是国君后撤,臣下进犯,他们就理曲了。”
于是晋军后撤了九十里,兑现了当年公子重耳对楚成王的承诺。楚军将士看到晋军后撤,想停止前进,但令尹成得臣不准。
晋军退避三舍后,驻扎在卫国的城濮,并在此与同盟诸侯会合备战。鲁僖公二十八年(公元前632年)四月初一,晋文公、宋成公、齐国上卿国归父、崔夭与秦穆公之子公子慭(yìn)各自率领本国的军队来到了城濮。晋国统帅的联军兵马已超过四万人,晋军后撤九十里,既报答了楚王当年的恩惠,又是诱敌深入、卑而骄之的计策,楚国令尹子玉(成得臣)中计了,他率领楚国兵马追击到了城濮。
楚军背靠着险要的丘陵扎营,晋文公有些担心,觉得楚军有地势之利。他在军营的大帐中来回踱步,忽然听到帐外有兵士在念诵说:“休耕的田地中绿草油油,舍弃旧的土地而犁锄新的田地。”这是在说,去年已经耕种过的旧田,今年不再耕种而使其休耕,而应去耕种已经休耕过的新田。文公也明白,这是在劝他丢掉对楚国的旧恩的顾虑,建立新的功业。就在文公还在犹豫之时,狐偃跑来对晋侯请战:“国君,出战吧!战而得胜,一定会得到诸侯的拥戴。如果不能胜,晋国外有黄河天险,内有高山屏障,也一定能够保证晋国的平安,不会有什么害处。”
晋文公还在说:“那楚国的恩惠怎么办?”
下军统帅栾枝说:“汉水以北的姬姓小国,楚国都把他们吞并完了,国君不能只想着小的恩惠而忘记大的耻辱。臣请一战!”
晋文公说:“寡人昨夜梦见与楚王搏斗,楚王伏在寡人的身上,咀嚼寡人的脑子,故而忧虑。”
狐偃说:“国君,此乃大吉!国君得到的是上天,楚王伏罪,而且我们柔服了楚国。”
楚令尹子玉派遣大夫斗勃来到晋军大营请战,他说:“楚军请与君王的勇士们做一次角斗的游戏,君王可靠在车驾上观看,楚令尹得臣请与国君一起观看。”
晋侯派栾枝回答道:“寡君听到了楚军的命令,楚王的恩惠,寡君未敢忘记,所以晋国的军队退到了这里。寡君以为大夫已经退兵了,臣下难道敢于抵抗国君吗?既然大夫不肯退兵,那就请大夫对贵部的将士们说:‘驾好你们的战车,忠于你们的国事,明天早晨战场上见!’”
初二日,晋军战车七百乘、将士近四万人,在城濮摆开了阵势,旌旗招展,严阵以待。先轸为元帅的中军居于中间,郤溱作为副帅辅佐;在中军的右面,是以狐毛为元帅的上军,狐偃作为副帅辅佐;以栾枝为元帅的下军排列在中军的左面,胥臣作为副帅辅佐。
晋文公则登上城濮的废墟俯瞰整个战场。他看到晋军神采飞扬,装备整齐,说:“晋国军马,无论年少与年长,皆合于礼,军心可用,此战必胜!”
楚军方面,后援的兵马已赶到城濮,总兵马有四万四千余人。总指挥是令尹成得臣,他率领若敖的六卒兵马作为中军;楚军左师由斗宜申统帅,郑国和许国的兵马跟随在楚左师之后;右师由大夫斗勃统帅,陈国和蔡国的军队跟随在楚国右师之后。令尹子玉自信地对楚军将领喊话:“诸君奋勇,今日之战后,晋国不复存在!”
晋军知己知彼,已经制订了此战的策略。楚军附属国军队的战斗力比楚军差,因此击鼓开战后,晋军按照事先制订好的战术,先由栾枝、胥臣率领晋军猛攻楚军右师后部的陈、蔡兵马,胥臣率领下军,以虎皮蒙在马上,向陈、蔡的军队冲击,陈、蔡兵马惊散奔逃,楚军右翼被晋军打乱,向后溃败。
楚军统帅令尹子玉看到右翼溃败,瞬间大怒,命中军和左军猛攻晋军的中路和右路。晋军右翼统帅狐毛设立两面将、佐大旗,让他们向后撤退;晋军左翼击溃楚军的右翼后,也用战车托着树枝带起大量沙尘、佯装撤退;楚令尹子玉命楚军组织兵力全力追击,楚国左军追击晋国右翼上军时,其中军跟进并不紧密,因此楚军左军的侧面暴露在晋国中军面前,晋国先轸、郤溱率领中军拦腰截击楚军的左翼,狐毛、狐偃此时率领处于晋军右翼、佯装后撤的上军突然杀个回马枪夹击冒进的楚军左翼兵马,斗宜申率领的楚军左翼也禁不住晋军两面夹击,向后溃败。此时位于楚中军的总指挥、令尹成得臣观察到楚军左、右两翼都已溃败,立即下令停止进攻,急速回撤,他保留了中军的有生力量没有溃败,率领残兵退出了战场。后来在返回楚国的路上,羞愧自杀。
晋军追击,攻占了楚军的营地后,停止前进,以作休整。晋军在楚军的营地内休整了三天,吃的是楚军丢弃的军营中的粮食,在四月初六日,晋军班师。
城濮大战,双方投入的总兵力达到了约八万人,最终以晋国的大胜结束。晋文公率领晋国从此建立起了霸权,成为了中原诸侯的霸主。楚国的势力则退出了中原,被迫回到了桐柏山、大别山以南地区活动。