I work for a company where several people are involved in FPGA development. We mainly use verilog / systemverilog (sometimes VHDL) to target different FPGA (not only Xilinx chip).
We use a Version Control Software (git, mercurial, ...) to track HDL code modification.
Each module and each project is versioned using a separate repository. And each deliverable version of each module or project is tagged (ex : v_1_1 or v_1_2)
Saying I work on a project named : my_project which uses 2 different modules (ex : foo and bar)
foo and bar are complex modules which have their own repository. Each module uses modules from other repository. Saying they both use qux module but at different versions.
Here is a summary :
my_project (my_project.git) @ v_0_1
foo (foo.git) @ v_3_2
qux (qux.git)@ v_2_7
bar (bar.git)@ v_1_6
qux (qux.git)@ v_4_5
foo and bar modules have been developed at very different time. They have been fully tested at these revisions (3.2 for foo, and 1.6 for bar) and we want to use these revisions. That why we do not want to upgrade qux in foo to use same revision than in bar.
In VHDL it's quite simple ... each module (at a given revision) is compiled in a separate library and then we can use the module from the library we want. Example : qux @ version 2.7 is compiled in library qux_lib_v_2_7, and qux @version 4.5 is compiled in library qux_lib_v_4_5. in foo we can call qux_lib_v_2_7.qux module and in bar we can call qux_lib_v_4_5.qux module.