Skip to main content
  • Home
  • Development
  • Documentation
  • Donate
  • Operational login
  • Browse the archive

swh logo
SoftwareHeritage
Software
Heritage
Archive
Features
  • Search

  • Downloads

  • Save code now

  • Add forge now

  • Help

https://github.com/git/git
06 June 2026, 22:11:42 UTC
  • Code
  • Branches (7)
  • Releases (1004)
  • Visits
    • Branches
    • Releases
    • HEAD
    • refs/heads/jch
    • refs/heads/maint
    • refs/heads/master
    • refs/heads/next
    • refs/heads/seen
    • refs/heads/todo
    • refs/notes/amlog
    • v2.54.0-rc2
    • v2.54.0-rc1
    • v2.54.0-rc0
    • v2.54.0
    • v2.53.0-rc2
    • v2.53.0-rc1
    • v2.53.0-rc0
    • v2.53.0
    • v2.52.0-rc2
    • v2.52.0-rc1
    • v2.52.0-rc0
    • v2.52.0
    • v2.51.2
    • v2.51.1
    • v2.51.0-rc2
    • v2.51.0-rc1
    • v2.51.0-rc0
    • v2.51.0
    • v2.50.1
    • v2.50.0-rc2
    • v2.50.0-rc1
    • v2.50.0-rc0
    • v2.50.0
    • v2.49.1
    • v2.49.0-rc2
    • v2.49.0-rc1
    • v2.49.0-rc0
    • v2.49.0
    • v2.48.2
    • v2.48.1
    • v2.48.0-rc2
    • v2.48.0-rc1
    • v2.48.0-rc0
    • v2.48.0
    • v2.47.3
    • v2.47.2
    • v2.47.1
    • v2.47.0-rc1
    • v2.47.0-rc0
    • v2.47.0
    • v2.46.4
    • v2.46.3
    • v2.46.2
    • v2.46.1
    • v2.46.0-rc2
    • v2.46.0-rc1
    • v2.46.0-rc0
    • v2.46.0
    • v2.45.4
    • v2.45.3
    • v2.45.2
    • v2.45.1
    • v2.45.0-rc1
    • v2.45.0-rc0
    • v2.45.0
    • v2.44.4
    • v2.44.3
    • v2.44.2
    • v2.44.1
    • v2.44.0-rc2
    • v2.44.0-rc1
    • v2.44.0-rc0
    • v2.44.0
    • v2.43.7
    • v2.43.6
    • v2.43.5
    • v2.43.4
    • v2.43.3
    • v2.43.2
    • v2.43.1
    • v2.43.0-rc2
    • v2.43.0-rc1
    • v2.43.0-rc0
    • v2.43.0
    • v2.42.4
    • v2.42.3
    • v2.42.2
    • v2.42.1
    • v2.42.0-rc2
    • v2.42.0-rc1
    • v2.42.0-rc0
    • v2.42.0
    • v2.41.3
    • v2.41.2
    • v2.41.1
    • v2.41.0-rc2
    • v2.41.0-rc1
    • v2.41.0-rc0
    • v2.41.0
    • v2.40.4
    • v2.40.3
    • v2.40.2
    • v2.40.1
    • v2.40.0-rc2
    • v2.40.0-rc1
    • v2.40.0-rc0
    • v2.40.0
    • v2.39.5
    • v2.39.4
    • v2.39.3
    • v2.39.2
    • v2.39.1
    • v2.39.0-rc2
    • v2.39.0-rc1
    • v2.39.0-rc0
    • v2.39.0
    • v2.38.5
    • v2.38.4
    • v2.38.3
    • v2.38.2
    • v2.38.1
    • v2.38.0-rc2
    • v2.38.0-rc1
    • v2.38.0-rc0
    • v2.38.0
    • v2.37.7
    • v2.37.6
    • v2.37.5
    • v2.37.4
    • v2.37.3
    • v2.37.2
    • v2.37.1
    • v2.37.0-rc2
    • v2.37.0-rc1
    • v2.37.0-rc0
    • v2.37.0
    • v2.36.6
    • v2.36.5
    • v2.36.4
    • v2.36.3
    • v2.36.2
    • v2.36.1
    • v2.36.0-rc2
    • v2.36.0-rc1
    • v2.36.0-rc0
    • v2.36.0
    • v2.35.8
    • v2.35.7
    • v2.35.6
    • v2.35.5
    • v2.35.4
    • v2.35.3
    • v2.35.2
    • v2.35.1
    • v2.35.0-rc2
    • v2.35.0-rc1
    • v2.35.0-rc0
    • v2.35.0
    • v2.34.8
    • v2.34.7
    • v2.34.6
    • v2.34.5
    • v2.34.4
    • v2.34.3
    • v2.34.2
    • v2.34.1
    • v2.34.0-rc2
    • v2.34.0-rc1
    • v2.34.0-rc0
    • v2.34.0
    • v2.33.8
    • v2.33.7
    • v2.33.6
    • v2.33.5
    • v2.33.4
    • v2.33.3
    • v2.33.2
    • v2.33.1
    • v2.33.0-rc2
    • v2.33.0-rc1
    • v2.33.0-rc0
    • v2.33.0
    • v2.32.7
    • v2.32.6
    • v2.32.5
    • v2.32.4
    • v2.32.3
    • v2.32.2
    • v2.32.1
    • v2.32.0-rc3
    • v2.32.0-rc2
    • v2.32.0-rc1
    • v2.32.0-rc0
    • v2.32.0
    • v2.31.8
    • v2.31.7
    • v2.31.6
    • v2.31.5
    • v2.31.4
    • v2.31.3
    • v2.31.2
    • v2.31.1
    • v2.31.0-rc2
    • v2.31.0-rc1
    • v2.31.0-rc0
    • v2.31.0
    • v2.30.9
    • v2.30.8
    • v2.30.7
    • v2.30.6
    • v2.30.5
    • v2.30.4
    • v2.30.3
    • v2.30.2
    • v2.30.1
    • v2.30.0-rc2
    • v2.30.0-rc1
    • v2.30.0-rc0
    • v2.30.0
    • v2.29.3
    • v2.29.2
    • v2.29.1
    • v2.29.0-rc2
    • v2.29.0-rc1
    • v2.29.0-rc0
    • v2.29.0
    • v2.28.1
    • v2.28.0-rc2
    • v2.28.0-rc1
    • v2.28.0-rc0
    • v2.28.0
    • v2.27.1
    • v2.27.0-rc2
    • v2.27.0-rc1
    • v2.27.0-rc0
    • v2.27.0
    • v2.26.3
    • v2.26.2
    • v2.26.1
    • v2.26.0-rc2
    • v2.26.0-rc1
    • v2.26.0-rc0
    • v2.26.0
    • v2.25.5
    • v2.25.4
    • v2.25.3
    • v2.25.2
    • v2.25.1
    • v2.25.0-rc2
    • v2.25.0-rc1
    • v2.25.0-rc0
    • v2.25.0
    • v2.24.4
    • v2.24.3
    • v2.24.2
    • v2.24.1
    • v2.24.0-rc2
    • v2.24.0-rc1
    • v2.24.0-rc0
    • v2.24.0
    • v2.23.4
    • v2.23.3
    • v2.23.2
    • v2.23.1
    • v2.23.0-rc2
    • v2.23.0-rc1
    • v2.23.0-rc0
    • v2.23.0
    • v2.22.5
    • v2.22.4
    • v2.22.3
    • v2.22.2
    • v2.22.1
    • v2.22.0-rc3
    • v2.22.0-rc2
    • v2.22.0-rc1
    • v2.22.0-rc0
    • v2.22.0
    • v2.21.4
    • v2.21.3
    • v2.21.2
    • v2.21.1
    • v2.21.0-rc2
    • v2.21.0-rc1
    • v2.21.0-rc0
    • v2.21.0
    • v2.20.5
    • v2.20.4
    • v2.20.3
    • v2.20.2
    • v2.20.1
    • v2.20.0-rc2
    • v2.20.0-rc1
    • v2.20.0-rc0
    • v2.20.0
    • v2.19.6
    • v2.19.5
    • v2.19.4
    • v2.19.3
    • v2.19.2
    • v2.19.1
    • v2.19.0-rc2
    • v2.19.0-rc1
    • v2.19.0-rc0
    • v2.19.0
    • v2.18.5
    • v2.18.4
    • v2.18.3
    • v2.18.2
    • v2.18.1
    • v2.18.0-rc2
    • v2.18.0-rc1
    • v2.18.0-rc0
    • v2.18.0
    • v2.17.6
    • v2.17.5
    • v2.17.4
    • v2.17.3
    • v2.17.2
    • v2.17.1
    • v2.17.0-rc2
    • v2.17.0-rc1
    • v2.17.0-rc0
    • v2.17.0
    • v2.16.6
    • v2.16.5
    • v2.16.4
    • v2.16.3
    • v2.16.2
    • v2.16.1
    • v2.16.0-rc2
    • v2.16.0-rc1
    • v2.16.0-rc0
    • v2.16.0
    • v2.15.4
    • v2.15.3
    • v2.15.2
    • v2.15.1
    • v2.15.0-rc2
    • v2.15.0-rc1
    • v2.15.0-rc0
    • v2.15.0
    • v2.14.6
    • v2.14.5
    • v2.14.4
    • v2.14.3
    • v2.14.2
    • v2.14.1
    • v2.14.0-rc1
    • v2.14.0-rc0
    • v2.14.0
    • v2.13.7
    • v2.13.6
    • v2.13.5
    • v2.13.4
    • v2.13.3
    • v2.13.2
    • v2.13.1
    • v2.13.0-rc2
    • v2.13.0-rc1
    • v2.13.0-rc0
    • v2.13.0
    • v2.12.5
    • v2.12.4
    • v2.12.3
    • v2.12.2
    • v2.12.1
    • v2.12.0-rc2
    • v2.12.0-rc1
    • v2.12.0-rc0
    • v2.12.0
    • v2.11.4
    • v2.11.3
    • v2.11.2
    • v2.11.1
    • v2.11.0-rc3
    • v2.11.0-rc2
    • v2.11.0-rc1
    • v2.11.0-rc0
    • v2.11.0
    • v2.10.5
    • v2.10.4
    • v2.10.3
    • v2.10.2
    • v2.10.1
    • v2.10.0-rc2
    • v2.10.0-rc1
    • v2.10.0-rc0
    • v2.10.0
    • v2.8.3
    • v2.8.2
    • v2.8.1
    • v2.8.0-rc4
    • v2.8.0-rc3
    • v2.8.0-rc2
    • v2.8.0-rc1
    • v2.8.0-rc0
    • v2.8.0
    • v2.7.6
    • v2.7.5
    • v2.7.4
    • v2.7.3
    • v2.7.2
    • v2.7.1
    • v2.7.0-rc3
    • v2.7.0-rc2
    • v2.7.0-rc1
    • v2.7.0-rc0
    • v2.7.0
    • v2.6.7
    • v2.6.6
    • v2.6.5
    • v2.6.4
    • v2.6.3
    • v2.6.2
    • v2.6.1
    • v2.6.0-rc3
    • v2.6.0-rc2
    • v2.6.0-rc1
    • v2.6.0-rc0
    • v2.6.0
    • v2.5.6
    • v2.5.5
    • v2.5.4
    • v2.5.3
    • v2.5.2
    • v2.5.1
    • v2.5.0-rc3
    • v2.5.0-rc2
    • v2.5.0-rc1
    • v2.5.0-rc0
    • v2.5.0
    • v2.4.12
    • v2.4.11
    • v2.4.10
    • v2.4.9
    • v2.4.8
    • v2.4.7
    • v2.4.6
    • v2.4.5
    • v2.4.4
    • v2.4.3
    • v2.4.2
    • v2.4.1
    • v2.4.0-rc3
    • v2.4.0-rc2
    • v2.4.0-rc1
    • v2.4.0-rc0
    • v2.4.0
    • v2.3.10
    • v2.3.9
    • v2.3.8
    • v2.3.7
    • v2.3.6
    • v2.3.5
    • v2.3.4
    • v2.3.3
    • v2.3.2
    • v2.3.1
    • v2.3.0-rc2
    • v2.3.0-rc1
    • v2.3.0-rc0
    • v2.3.0
    • v2.2.3
    • v2.2.2
    • v2.2.1
    • v2.2.0-rc3
    • v2.2.0-rc2
    • v2.2.0-rc1
    • v2.2.0-rc0
    • v2.2.0
    • v2.1.4
    • v2.1.3
    • v2.1.2
    • v2.1.1
    • v2.1.0-rc2
    • v2.1.0-rc1
    • v2.1.0-rc0
    • v2.1.0
    • v2.0.5
    • v2.0.4
    • v2.0.3
    • v2.0.2
    • v2.0.1
    • v2.0.0-rc4
    • v2.0.0-rc3
    • v2.0.0-rc2
    • v2.0.0-rc1
    • v2.0.0-rc0
    • v2.0.0
    • v1.9-rc2
    • v1.9-rc1
    • v1.9-rc0
    • v1.9.5
    • v1.9.4
    • v1.9.3
    • v1.9.2
    • v1.9.1
    • v1.9.0-rc3
    • v1.9.0
    • v1.8.5-rc3
    • v1.8.5-rc2
    • v1.8.5-rc1
    • v1.8.5-rc0
    • v1.8.5.6
    • v1.8.5.5
    • v1.8.5.4
    • v1.8.5.3
    • v1.8.5.2
    • v1.8.5.1
    • v1.8.5
    • v1.8.4-rc4
    • v1.8.4-rc3
    • v1.8.4-rc2
    • v1.8.4-rc1
    • v1.8.4-rc0
    • v1.8.4.5
    • v1.8.4.4
    • v1.8.4.3
    • v1.8.4.2
    • v1.8.4.1
    • v1.8.4
    • v1.8.3-rc3
    • v1.8.3-rc2
    • v1.8.3-rc1
    • v1.8.3-rc0
    • v1.8.3.4
    • v1.8.3.3
    • v1.8.3.2
    • v1.8.3.1
    • v1.8.3
    • v1.8.2-rc3
    • v1.8.2-rc2
    • v1.8.2-rc1
    • v1.8.2-rc0
    • v1.8.2.3
    • v1.8.2.2
    • v1.8.2.1
    • v1.8.2
    • v1.8.1-rc3
    • v1.8.1-rc2
    • v1.8.1-rc1
    • v1.8.1-rc0
    • v1.8.1.6
    • v1.8.1.5
    • v1.8.1.4
    • v1.8.1.3
    • v1.8.1.2
    • v1.8.1.1
    • v1.8.1
    • v1.8.0-rc3
    • v1.8.0-rc2
    • v1.8.0-rc1
    • v1.8.0-rc0
    • v1.8.0.3
    • v1.8.0.2
    • v1.8.0.1
    • v1.8.0
    • v1.7.12-rc3
    • v1.7.12-rc2
    • v1.7.12-rc1
    • v1.7.12-rc0
    • v1.7.12.4
    • v1.7.12.3
    • v1.7.12.2
    • v1.7.12.1
    • v1.7.12
    • v1.7.11-rc3
    • v1.7.11-rc2
    • v1.7.11-rc1
    • v1.7.11-rc0
    • v1.7.11.7
    • v1.7.11.6
    • v1.7.11.5
    • v1.7.11.4
    • v1.7.11.3
    • v1.7.11.2
    • v1.7.11.1
    • v1.7.11
    • v1.7.10-rc4
    • v1.7.10-rc3
    • v1.7.10-rc2
    • v1.7.10-rc1
    • v1.7.10-rc0
    • v1.7.10.5
    • v1.7.10.4
    • v1.7.10.3
    • v1.7.10.2
    • v1.7.10.1
    • v1.7.10
    • v1.7.9-rc2
    • v1.7.9-rc1
    • v1.7.9-rc0
    • v1.7.9.7
    • v1.7.9.6
    • v1.7.9.5
    • v1.7.9.4
    • v1.7.9.3
    • v1.7.9.2
    • v1.7.9.1
    • v1.7.9
    • v1.7.8-rc4
    • v1.7.8-rc3
    • v1.7.8-rc2
    • v1.7.8-rc1
    • v1.7.8-rc0
    • v1.7.8.6
    • v1.7.8.5
    • v1.7.8.4
    • v1.7.8.3
    • v1.7.8.2
    • v1.7.8.1
    • v1.7.8
    • v1.7.7-rc3
    • v1.7.7-rc2
    • v1.7.7-rc1
    • v1.7.7-rc0
    • v1.7.7.7
    • v1.7.7.6
    • v1.7.7.5
    • v1.7.7.4
    • v1.7.7.3
    • v1.7.7.2
    • v1.7.7.1
    • v1.7.7
    • v1.7.6-rc3
    • v1.7.6-rc2
    • v1.7.6-rc1
    • v1.7.6-rc0
    • v1.7.6.6
    • v1.7.6.5
    • v1.7.6.4
    • v1.7.6.3
    • v1.7.6.2
    • v1.7.6.1
    • v1.7.6
    • v1.7.5-rc3
    • v1.7.5-rc2
    • v1.7.5-rc1
    • v1.7.5-rc0
    • v1.7.5.4
    • v1.7.5.3
    • v1.7.5.2
    • v1.7.5.1
    • v1.7.5
    • v1.7.4-rc3
    • v1.7.4-rc2
    • v1.7.4-rc1
    • v1.7.4-rc0
    • v1.7.4.5
    • v1.7.4.4
    • v1.7.4.3
    • v1.7.4.2
    • v1.7.4.1
    • v1.7.4
    • v1.7.3-rc2
    • v1.7.3-rc1
    • v1.7.3-rc0
    • v1.7.3.5
    • v1.7.3.4
    • v1.7.3.3
    • v1.7.3.2
    • v1.7.3.1
    • v1.7.3
    • v1.7.2-rc3
    • v1.7.2-rc2
    • v1.7.2-rc1
    • v1.7.2-rc0
    • v1.7.2.5
    • v1.7.2.4
    • v1.7.2.3
    • v1.7.2.2
    • v1.7.2.1
    • v1.7.2
    • v1.7.1-rc2
    • v1.7.1-rc1
    • v1.7.1-rc0
    • v1.7.1.4
    • v1.7.1.3
    • v1.7.1.2
    • v1.7.1.1
    • v1.7.1
    • v1.7.0-rc2
    • v1.7.0-rc1
    • v1.7.0-rc0
    • v1.7.0.9
    • v1.7.0.8
    • v1.7.0.7
    • v1.7.0.6
    • v1.7.0.5
    • v1.7.0.4
    • v1.7.0.3
    • v1.7.0.2
    • v1.7.0.1
    • v1.7.0
    • v1.6.6-rc4
    • v1.6.6-rc3
    • v1.6.6-rc2
    • v1.6.6-rc1
    • v1.6.6-rc0
    • v1.6.6.3
    • v1.6.6.2
    • v1.6.6.1
    • v1.6.6
    • v1.6.5-rc3
    • v1.6.5-rc2
    • v1.6.5-rc1
    • v1.6.5-rc0
    • v1.6.5.9
    • v1.6.5.8
    • v1.6.5.7
    • v1.6.5.6
    • v1.6.5.5
    • v1.6.5.4
    • v1.6.5.3
    • v1.6.5.2
    • v1.6.5.1
    • v1.6.5
    • v1.6.4-rc3
    • v1.6.4-rc2
    • v1.6.4-rc1
    • v1.6.4-rc0
    • v1.6.4.5
    • v1.6.4.4
    • v1.6.4.3
    • v1.6.4.2
    • v1.6.4.1
    • v1.6.4
    • v1.6.3-rc4
    • v1.6.3-rc3
    • v1.6.3-rc2
    • v1.6.3-rc1
    • v1.6.3-rc0
    • v1.6.3.4
    • v1.6.3.3
    • v1.6.3.2
    • v1.6.3.1
    • v1.6.3
    • v1.6.2-rc2
    • v1.6.2-rc1
    • v1.6.2-rc0
    • v1.6.2.5
    • v1.6.2.4
    • v1.6.2.3
    • v1.6.2.2
    • v1.6.2.1
    • v1.6.2
    • v1.6.1-rc4
    • v1.6.1-rc3
    • v1.6.1-rc2
    • v1.6.1-rc1
    • v1.6.1.4
    • v1.6.1.3
    • v1.6.1.2
    • v1.6.1.1
    • v1.6.1
    • v1.6.0-rc3
    • v1.6.0-rc2
    • v1.6.0-rc1
    • v1.6.0-rc0
    • v1.6.0.6
    • v1.6.0.5
    • v1.6.0.4
    • v1.6.0.3
    • v1.6.0.2
    • v1.6.0.1
    • v1.6.0
    • v1.5.6-rc3
    • v1.5.6-rc2
    • v1.5.6-rc1
    • v1.5.6-rc0
    • v1.5.6.6
    • v1.5.6.5
    • v1.5.6.4
    • v1.5.6.3
    • v1.5.6.2
    • v1.5.6.1
    • v1.5.6
    • v1.5.5-rc3
    • v1.5.5-rc2
    • v1.5.5-rc1
    • v1.5.5-rc0
    • v1.5.5.6
    • v1.5.5.5
    • v1.5.5.4
    • v1.5.5.3
    • v1.5.5.2
    • v1.5.5.1
    • v1.5.5
    • v1.5.4-rc5
    • v1.5.4-rc4
    • v1.5.4-rc3
    • v1.5.4-rc2
    • v1.5.4-rc1
    • v1.5.4-rc0
    • v1.5.4.7
    • v1.5.4.6
    • v1.5.4.5
    • v1.5.4.4
    • v1.5.4.3
    • v1.5.4.2
    • v1.5.4.1
    • v1.5.4
    • v1.5.3-rc7
    • v1.5.3-rc6
    • v1.5.3-rc5
    • v1.5.3-rc4
    • v1.5.3-rc3
    • v1.5.3-rc2
    • v1.5.3-rc1
    • v1.5.3-rc0
    • v1.5.3.8
    • v1.5.3.7
    • v1.5.3.6
    • v1.5.3.5
    • v1.5.3.4
    • v1.5.3.3
    • v1.5.3.2
    • v1.5.3.1
    • v1.5.3
    • v1.5.2-rc3
    • v1.5.2-rc2
    • v1.5.2-rc1
    • v1.5.2-rc0
    • v1.5.2.5
    • v1.5.2.4
    • v1.5.2.3
    • v1.5.2.2
    • v1.5.2.1
    • v1.5.2
    • v1.5.1-rc3
    • v1.5.1-rc2
    • v1.5.1-rc1
    • v1.5.1.6
    • v1.5.1.5
    • v1.5.1.4
    • v1.5.1.3
    • v1.5.1.2
    • v1.5.1.1
    • v1.5.1
    • v1.5.0-rc4
    • v1.5.0-rc3
    • v1.5.0-rc2
    • v1.5.0-rc1
    • v1.5.0-rc0
    • v1.5.0.7
    • v1.5.0.6
    • v1.5.0.5
    • v1.5.0.4
    • v1.5.0.3
    • v1.5.0.2
    • v1.5.0.1
    • v1.5.0
    • v1.4.4-rc2
    • v1.4.4-rc1
    • v1.4.4.5
    • v1.4.4.4
    • v1.4.4.3
    • v1.4.4.2
    • v1.4.4.1
    • v1.4.4
    • v1.4.3-rc3
    • v1.4.3-rc2
    • v1.4.3-rc1
    • v1.4.3.5
    • v1.4.3.4
    • v1.4.3.3
    • v1.4.3.2
    • v1.4.3.1
    • v1.4.3
    • v1.4.2-rc4
    • v1.4.2-rc3
    • v1.4.2-rc2
    • v1.4.2-rc1
    • v1.4.2.4
    • v1.4.2.3
    • v1.4.2.2
    • v1.4.2.1
    • v1.4.2
    • v1.4.1-rc2
    • v1.4.1-rc1
    • v1.4.1.1
    • v1.4.1
    • v1.4.0-rc2
    • v1.4.0-rc1
    • v1.4.0
    • v1.3.3
    • v1.3.2
    • v1.3.1
    • v1.3.0-rc4
    • v1.3.0-rc3
    • v1.3.0-rc2
    • v1.3.0-rc1
    • v1.3.0
    • v1.2.6
    • v1.2.5
    • v1.2.4
    • v1.2.3
    • v1.2.2
    • v1.2.1
    • v1.2.0
    • v1.1.6
    • v1.1.5
    • v1.1.4
    • v1.1.3
    • v1.1.2
    • v1.1.1
    • v1.1.0
    • v1.0.13
    • v1.0.12
    • v1.0.11
    • v1.0.10
    • v1.0.9
    • v1.0.8
    • v1.0.7
    • v1.0.6
    • v1.0.5
    • v1.0.4
    • v1.0.3
    • v1.0.0b
    • v1.0.0a
    • v1.0.0
    • v0.99.9n
    • v0.99.9m
    • v0.99.9l
    • v0.99.9k
    • v0.99.9j
    • v0.99.9i
    • v0.99.9h
    • v0.99.9g
    • v0.99.9f
    • v0.99.9e
    • v0.99.9d
    • v0.99.9c
    • v0.99.9b
    • v0.99.9a
    • v0.99.9
    • v0.99.8g
    • v0.99.8f
    • v0.99.8e
    • v0.99.8d
    • v0.99.8c
    • v0.99.8b
    • v0.99.8a
    • v0.99.8
    • v0.99.7d
    • v0.99.7c
    • v0.99.7b
    • v0.99.7a
    • v0.99.7
    • v0.99.6
    • v0.99.5
    • v0.99.4
    • v0.99.3
    • v0.99.2
    • v0.99.1
    • v0.99
    • gitgui-0.21.0
    • gitgui-0.20.0
    • gitgui-0.19.0
    • gitgui-0.18.0
    • gitgui-0.17.0
    • gitgui-0.16.0
    • gitgui-0.15.0
    • gitgui-0.14.0
    • gitgui-0.13.0
    • gitgui-0.12.0
    • gitgui-0.11.0
    • gitgui-0.10.2
    • gitgui-0.10.1
    • gitgui-0.10.0
    • gitgui-0.9.3
    • gitgui-0.9.2
    • gitgui-0.9.1
    • gitgui-0.9.0
    • gitgui-0.8.4
    • gitgui-0.8.3
    • gitgui-0.8.2
    • gitgui-0.8.1
    • gitgui-0.8.0
    • gitgui-0.7.5
    • gitgui-0.7.4
    • gitgui-0.7.3
    • gitgui-0.7.2
    • gitgui-0.7.1
    • gitgui-0.7.0-rc1
    • gitgui-0.7.0
    • gitgui-0.6.5
    • gitgui-0.6.4
    • gitgui-0.6.3
    • gitgui-0.6.2
    • gitgui-0.6.1
    • gitgui-0.6.0
    • Releases list truncated to 992 entries, 12 were omitted.
  • 2f0008c
  • /
  • git.c
Raw File Download Save again
Take a new snapshot of a software origin

If the archived software origin currently browsed is not synchronized with its upstream version (for instance when new commits have been issued), you can explicitly request Software Heritage to take a new snapshot of it.

Use the form below to proceed. Once a request has been submitted and accepted, it will be processed as soon as possible. You can then check its processing state by visiting this dedicated page.
swh spinner

Processing "take a new snapshot" request ...

To reference or cite the objects present in the Software Heritage archive, permalinks based on SoftWare Hash IDentifiers (SWHIDs) must be used.
Select below a type of object currently browsed in order to display its associated SWHID and permalink.

  • content
  • directory
  • revision
  • snapshot
origin badgecontent badge
swh:1:cnt:36f08891ef5476ec446657d9f489b3b67dc916e5
origin badgedirectory badge
swh:1:dir:2f0008c0002043dfed547993d8df485889725ba7
origin badgerevision badge
swh:1:rev:9ac3f193c05c2237e2b14ebaa1149e9fc8a1abe0
origin badgesnapshot badge
swh:1:snp:30e3451e0b359f1a21c4fdc1ae26bd9d78f6cff7

This interface enables to generate software citations, provided that the root directory of browsed objects contains a citation.cff or codemeta.json file.
Select below a type of object currently browsed in order to generate citations for them.

  • content
  • directory
  • revision
  • snapshot
(requires biblatex-software package)
Generating citation ...
(requires biblatex-software package)
Generating citation ...
(requires biblatex-software package)
Generating citation ...
(requires biblatex-software package)
Generating citation ...
Tip revision: 9ac3f193c05c2237e2b14ebaa1149e9fc8a1abe0 authored by Junio C Hamano on 02 June 2026, 03:48:59 UTC
The 11th batch
Tip revision: 9ac3f19
git.c
#define USE_THE_REPOSITORY_VARIABLE

#include "builtin.h"
#include "config.h"
#include "environment.h"
#include "exec-cmd.h"
#include "gettext.h"
#include "help.h"
#include "object-file.h"
#include "pager.h"
#include "read-cache-ll.h"
#include "run-command.h"
#include "alias.h"
#include "replace-object.h"
#include "setup.h"
#include "attr.h"
#include "shallow.h"
#include "trace.h"
#include "trace2.h"

#define RUN_SETUP		(1<<0)
#define RUN_SETUP_GENTLY	(1<<1)
#define USE_PAGER		(1<<2)
/*
 * require working tree to be present -- anything uses this needs
 * RUN_SETUP for reading from the configuration file.
 */
#define NEED_WORK_TREE		(1<<3)
#define DELAY_PAGER_CONFIG	(1<<4)
#define NO_PARSEOPT		(1<<5) /* parse-options is not used */
#define DEPRECATED		(1<<6)

struct cmd_struct {
	const char *cmd;
	int (*fn)(int, const char **, const char *, struct repository *);
	unsigned int option;
};

const char git_usage_string[] =
	N_("git [-v | --version] [-h | --help] [-C <path>] [-c <name>=<value>]\n"
	   "           [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]\n"
	   "           [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--no-lazy-fetch]\n"
	   "           [--no-optional-locks] [--no-advice] [--bare] [--git-dir=<path>]\n"
	   "           [--work-tree=<path>] [--namespace=<name>] [--config-env=<name>=<envvar>]\n"
	   "           <command> [<args>]");

const char git_more_info_string[] =
	N_("'git help -a' and 'git help -g' list available subcommands and some\n"
	   "concept guides. See 'git help <command>' or 'git help <concept>'\n"
	   "to read about a specific subcommand or concept.\n"
	   "See 'git help git' for an overview of the system.");

static int use_pager = -1;

static void list_builtins(struct string_list *list,
			  unsigned int include_option,
			  unsigned int exclude_option);

static void exclude_helpers_from_list(struct string_list *list)
{
	size_t i = 0;

	while (i < list->nr) {
		if (strstr(list->items[i].string, "--"))
			unsorted_string_list_delete_item(list, i, 0);
		else
			i++;
	}
}

static int match_token(const char *spec, int len, const char *token)
{
	int token_len = strlen(token);

	return len == token_len && !strncmp(spec, token, token_len);
}

static int list_cmds(const char *spec)
{
	struct string_list list = STRING_LIST_INIT_DUP;
	int nongit;

	/*
	* Set up the repository so we can pick up any repo-level config (like
	* completion.commands).
	*/
	setup_git_directory_gently(the_repository, &nongit);

	while (*spec) {
		const char *sep = strchrnul(spec, ',');
		int len = sep - spec;

		if (match_token(spec, len, "builtins"))
			list_builtins(&list, 0, 0);
		else if (match_token(spec, len, "main"))
			list_all_main_cmds(&list);
		else if (match_token(spec, len, "others"))
			list_all_other_cmds(&list);
		else if (match_token(spec, len, "nohelpers"))
			exclude_helpers_from_list(&list);
		else if (match_token(spec, len, "alias"))
			list_aliases(&list);
		else if (match_token(spec, len, "config"))
			list_cmds_by_config(&list);
		else if (match_token(spec, len, "deprecated"))
			list_builtins(&list, DEPRECATED, 0);
		else if (len > 5 && !strncmp(spec, "list-", 5)) {
			struct strbuf sb = STRBUF_INIT;

			strbuf_add(&sb, spec + 5, len - 5);
			list_cmds_by_category(&list, sb.buf);
			strbuf_release(&sb);
		}
		else
			die(_("unsupported command listing type '%s'"), spec);
		spec += len;
		if (*spec == ',')
			spec++;
	}
	for (size_t i = 0; i < list.nr; i++)
		puts(list.items[i].string);
	string_list_clear(&list, 1);
	return 0;
}

static void commit_pager_choice(void)
{
	switch (use_pager) {
	case 0:
		setenv("GIT_PAGER", "cat", 1);
		break;
	case 1:
		setup_pager(the_repository);
		break;
	default:
		break;
	}
}

void setup_auto_pager(const char *cmd, int def)
{
	if (use_pager != -1 || pager_in_use())
		return;
	use_pager = check_pager_config(the_repository, cmd);
	if (use_pager == -1)
		use_pager = def;
	commit_pager_choice();
}

static void print_system_path(const char *path)
{
	char *s_path = system_path(path);
	puts(s_path);
	free(s_path);
}

static int handle_options(const char ***argv, int *argc, int *envchanged)
{
	const char **orig_argv = *argv;

	while (*argc > 0) {
		const char *cmd = (*argv)[0];
		if (cmd[0] != '-')
			break;

		/*
		 * For legacy reasons, the "version" and "help"
		 * commands can be written with "--" prepended
		 * to make them look like flags.
		 */
		if (!strcmp(cmd, "--help") || !strcmp(cmd, "-h") ||
		    !strcmp(cmd, "--version") || !strcmp(cmd, "-v"))
			break;

		/*
		 * Check remaining flags.
		 */
		if (skip_prefix(cmd, "--exec-path", &cmd)) {
			if (*cmd == '=')
				git_set_exec_path(cmd + 1);
			else {
				puts(git_exec_path());
				trace2_cmd_name("_query_");
				exit(0);
			}
		} else if (!strcmp(cmd, "--html-path")) {
			print_system_path(GIT_HTML_PATH);
			trace2_cmd_name("_query_");
			exit(0);
		} else if (!strcmp(cmd, "--man-path")) {
			print_system_path(GIT_MAN_PATH);
			trace2_cmd_name("_query_");
			exit(0);
		} else if (!strcmp(cmd, "--info-path")) {
			print_system_path(GIT_INFO_PATH);
			trace2_cmd_name("_query_");
			exit(0);
		} else if (!strcmp(cmd, "-p") || !strcmp(cmd, "--paginate")) {
			use_pager = 1;
		} else if (!strcmp(cmd, "-P") || !strcmp(cmd, "--no-pager")) {
			use_pager = 0;
			if (envchanged)
				*envchanged = 1;
		} else if (!strcmp(cmd, "--no-lazy-fetch")) {
			fetch_if_missing = 0;
			setenv(NO_LAZY_FETCH_ENVIRONMENT, "1", 1);
			if (envchanged)
				*envchanged = 1;
		} else if (!strcmp(cmd, "--no-replace-objects")) {
			disable_replace_refs();
			setenv(NO_REPLACE_OBJECTS_ENVIRONMENT, "1", 1);
			if (envchanged)
				*envchanged = 1;
		} else if (!strcmp(cmd, "--git-dir")) {
			if (*argc < 2) {
				fprintf(stderr, _("no directory given for '%s' option\n" ), "--git-dir");
				usage(git_usage_string);
			}
			setenv(GIT_DIR_ENVIRONMENT, (*argv)[1], 1);
			if (envchanged)
				*envchanged = 1;
			(*argv)++;
			(*argc)--;
		} else if (skip_prefix(cmd, "--git-dir=", &cmd)) {
			setenv(GIT_DIR_ENVIRONMENT, cmd, 1);
			if (envchanged)
				*envchanged = 1;
		} else if (!strcmp(cmd, "--namespace")) {
			if (*argc < 2) {
				fprintf(stderr, _("no namespace given for --namespace\n" ));
				usage(git_usage_string);
			}
			setenv(GIT_NAMESPACE_ENVIRONMENT, (*argv)[1], 1);
			if (envchanged)
				*envchanged = 1;
			(*argv)++;
			(*argc)--;
		} else if (skip_prefix(cmd, "--namespace=", &cmd)) {
			setenv(GIT_NAMESPACE_ENVIRONMENT, cmd, 1);
			if (envchanged)
				*envchanged = 1;
		} else if (!strcmp(cmd, "--work-tree")) {
			if (*argc < 2) {
				fprintf(stderr, _("no directory given for '%s' option\n" ), "--work-tree");
				usage(git_usage_string);
			}
			setenv(GIT_WORK_TREE_ENVIRONMENT, (*argv)[1], 1);
			if (envchanged)
				*envchanged = 1;
			(*argv)++;
			(*argc)--;
		} else if (skip_prefix(cmd, "--work-tree=", &cmd)) {
			setenv(GIT_WORK_TREE_ENVIRONMENT, cmd, 1);
			if (envchanged)
				*envchanged = 1;
		} else if (!strcmp(cmd, "--bare")) {
			char *cwd = xgetcwd();
			is_bare_repository_cfg = 1;
			setenv(GIT_DIR_ENVIRONMENT, cwd, 0);
			free(cwd);
			setenv(GIT_IMPLICIT_WORK_TREE_ENVIRONMENT, "0", 1);
			if (envchanged)
				*envchanged = 1;
		} else if (!strcmp(cmd, "-c")) {
			if (*argc < 2) {
				fprintf(stderr, _("-c expects a configuration string\n" ));
				usage(git_usage_string);
			}
			git_config_push_parameter((*argv)[1]);
			(*argv)++;
			(*argc)--;
		} else if (!strcmp(cmd, "--config-env")) {
			if (*argc < 2) {
				fprintf(stderr, _("no config key given for --config-env\n" ));
				usage(git_usage_string);
			}
			git_config_push_env((*argv)[1]);
			(*argv)++;
			(*argc)--;
		} else if (skip_prefix(cmd, "--config-env=", &cmd)) {
			git_config_push_env(cmd);
		} else if (!strcmp(cmd, "--literal-pathspecs")) {
			setenv(GIT_LITERAL_PATHSPECS_ENVIRONMENT, "1", 1);
			if (envchanged)
				*envchanged = 1;
		} else if (!strcmp(cmd, "--no-literal-pathspecs")) {
			setenv(GIT_LITERAL_PATHSPECS_ENVIRONMENT, "0", 1);
			if (envchanged)
				*envchanged = 1;
		} else if (!strcmp(cmd, "--glob-pathspecs")) {
			setenv(GIT_GLOB_PATHSPECS_ENVIRONMENT, "1", 1);
			if (envchanged)
				*envchanged = 1;
		} else if (!strcmp(cmd, "--noglob-pathspecs")) {
			setenv(GIT_NOGLOB_PATHSPECS_ENVIRONMENT, "1", 1);
			if (envchanged)
				*envchanged = 1;
		} else if (!strcmp(cmd, "--icase-pathspecs")) {
			setenv(GIT_ICASE_PATHSPECS_ENVIRONMENT, "1", 1);
			if (envchanged)
				*envchanged = 1;
		} else if (!strcmp(cmd, "--no-optional-locks")) {
			setenv(GIT_OPTIONAL_LOCKS_ENVIRONMENT, "0", 1);
			if (envchanged)
				*envchanged = 1;
		} else if (!strcmp(cmd, "--shallow-file")) {
			(*argv)++;
			(*argc)--;
			set_alternate_shallow_file(the_repository, (*argv)[0], 1);
			if (envchanged)
				*envchanged = 1;
		} else if (!strcmp(cmd, "-C")) {
			if (*argc < 2) {
				fprintf(stderr, _("no directory given for '%s' option\n" ), "-C");
				usage(git_usage_string);
			}
			if ((*argv)[1][0]) {
				if (chdir((*argv)[1]))
					die_errno("cannot change to '%s'", (*argv)[1]);
				if (envchanged)
					*envchanged = 1;
			}
			(*argv)++;
			(*argc)--;
		} else if (skip_prefix(cmd, "--list-cmds=", &cmd)) {
			trace2_cmd_name("_query_");
			if (!strcmp(cmd, "parseopt")) {
				struct string_list list = STRING_LIST_INIT_DUP;

				list_builtins(&list, 0, NO_PARSEOPT);
				for (size_t i = 0; i < list.nr; i++)
					printf("%s ", list.items[i].string);
				string_list_clear(&list, 0);
				exit(0);
			} else {
				exit(list_cmds(cmd));
			}
		} else if (!strcmp(cmd, "--attr-source")) {
			if (*argc < 2) {
				fprintf(stderr, _("no attribute source given for --attr-source\n" ));
				usage(git_usage_string);
			}
			setenv(GIT_ATTR_SOURCE_ENVIRONMENT, (*argv)[1], 1);
			if (envchanged)
				*envchanged = 1;
			(*argv)++;
			(*argc)--;
		} else if (skip_prefix(cmd, "--attr-source=", &cmd)) {
			set_git_attr_source(cmd);
			setenv(GIT_ATTR_SOURCE_ENVIRONMENT, cmd, 1);
			if (envchanged)
				*envchanged = 1;
		} else if (!strcmp(cmd, "--no-advice")) {
			setenv(GIT_ADVICE_ENVIRONMENT, "0", 1);
			if (envchanged)
				*envchanged = 1;
		} else {
			fprintf(stderr, _("unknown option: %s\n"), cmd);
			usage(git_usage_string);
		}

		(*argv)++;
		(*argc)--;
	}
	return (*argv) - orig_argv;
}

static int handle_alias(struct strvec *args, struct string_list *expanded_aliases)
{
	int envchanged = 0, ret = 0, saved_errno = errno;
	int count, option_count;
	const char **new_argv;
	const char *alias_command;
	char *alias_string;

	alias_command = args->v[0];
	alias_string = alias_lookup(alias_command);
	if (alias_string) {
		struct string_list_item *seen;

		if (args->nr == 2 && !strcmp(args->v[1], "-h"))
			fprintf_ln(stderr, _("'%s' is aliased to '%s'"),
				   alias_command, alias_string);
		if (alias_string[0] == '!') {
			struct child_process child = CHILD_PROCESS_INIT;
			int nongit_ok;

			/* Aliases expect GIT_PREFIX, GIT_DIR etc to be set */
			setup_git_directory_gently(the_repository, &nongit_ok);

			commit_pager_choice();

			child.use_shell = 1;
			child.clean_on_exit = 1;
			child.wait_after_clean = 1;
			child.trace2_child_class = "shell_alias";
			strvec_push(&child.args, alias_string + 1);
			strvec_pushv(&child.args, args->v + 1);

			trace2_cmd_alias(alias_command, child.args.v);
			trace2_cmd_name("_run_shell_alias_");

			ret = run_command(&child);
			if (ret >= 0)   /* normal exit */
				exit(ret);

			die_errno(_("while expanding alias '%s': '%s'"),
				  alias_command, alias_string + 1);
		}
		count = split_cmdline(alias_string, &new_argv);
		if (count < 0)
			die(_("bad alias.%s string: %s"), alias_command,
			    _(split_cmdline_strerror(count)));
		option_count = handle_options(&new_argv, &count, &envchanged);
		if (envchanged)
			die(_("alias '%s' changes environment variables.\n"
			      "You can use '!git' in the alias to do this"),
			    alias_command);
		MOVE_ARRAY(new_argv - option_count, new_argv, count);
		new_argv -= option_count;

		if (count < 1)
			die(_("empty alias for %s"), alias_command);

		if (!strcmp(alias_command, new_argv[0]))
			die(_("recursive alias: %s"), alias_command);

		string_list_append(expanded_aliases, alias_command);
		seen = unsorted_string_list_lookup(expanded_aliases,
						   new_argv[0]);

		if (seen) {
			struct strbuf sb = STRBUF_INIT;
			for (size_t i = 0; i < expanded_aliases->nr; i++) {
				struct string_list_item *item = &expanded_aliases->items[i];

				strbuf_addf(&sb, "\n  %s", item->string);
				if (item == seen)
					strbuf_addstr(&sb, " <==");
				else if (i == expanded_aliases->nr - 1)
					strbuf_addstr(&sb, " ==>");
			}
			die(_("alias loop detected: expansion of '%s' does"
			      " not terminate:%s"), expanded_aliases->items[0].string, sb.buf);
		}

		trace_argv_printf(new_argv,
				  "trace: alias expansion: %s =>",
				  alias_command);
		trace2_cmd_alias(alias_command, new_argv);

		/* Replace the alias with the new arguments. */
		strvec_splice(args, 0, 1, new_argv, count);

		free(alias_string);
		free(new_argv);

		ret = 1;
	}

	errno = saved_errno;

	return ret;
}

static int run_builtin(struct cmd_struct *p, int argc, const char **argv, struct repository *repo)
{
	int status, help;
	int no_repo = 1;
	struct stat st;
	const char *prefix;
	int run_setup = (p->option & (RUN_SETUP | RUN_SETUP_GENTLY));

	help = argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help-all"));
	if (help && (run_setup & RUN_SETUP))
		/* demote to GENTLY to allow 'git cmd -h' outside repo */
		run_setup = RUN_SETUP_GENTLY;

	if (run_setup & RUN_SETUP) {
		prefix = setup_git_directory(the_repository);
		no_repo = 0;
	} else if (run_setup & RUN_SETUP_GENTLY) {
		prefix = setup_git_directory_gently(the_repository, &no_repo);
	} else {
		prefix = NULL;
	}
	assert(!prefix || *prefix);
	precompose_argv_prefix(argc, argv, NULL);
	if (use_pager == -1 && run_setup &&
		!(p->option & DELAY_PAGER_CONFIG))
		use_pager = check_pager_config(repo, p->cmd);
	if (use_pager == -1 && p->option & USE_PAGER)
		use_pager = 1;
	if (run_setup && startup_info->have_repository)
		/* get_git_dir() may set up repo, avoid that */
		trace_repo_setup(repo);
	commit_pager_choice();

	if (!help && p->option & NEED_WORK_TREE)
		setup_work_tree(the_repository);

	trace_argv_printf(argv, "trace: built-in: git");
	trace2_cmd_name(p->cmd);

	validate_cache_entries(repo->index);
	status = p->fn(argc, argv, prefix, no_repo ? NULL : repo);
	validate_cache_entries(repo->index);

	if (status)
		return status;

	/* Somebody closed stdout? */
	if (fstat(fileno(stdout), &st))
		return 0;
	/* Ignore write errors for pipes and sockets.. */
	if (S_ISFIFO(st.st_mode) || S_ISSOCK(st.st_mode))
		return 0;

	/* Check for ENOSPC and EIO errors.. */
	if (fflush(stdout))
		die_errno(_("write failure on standard output"));
	if (ferror(stdout))
		die(_("unknown write failure on standard output"));
	if (fclose(stdout))
		die_errno(_("close failed on standard output"));
	return 0;
}

static struct cmd_struct commands[] = {
	{ "add", cmd_add, RUN_SETUP | NEED_WORK_TREE },
	{ "am", cmd_am, RUN_SETUP | NEED_WORK_TREE },
	{ "annotate", cmd_annotate, RUN_SETUP },
	{ "apply", cmd_apply, RUN_SETUP_GENTLY },
	{ "archive", cmd_archive, RUN_SETUP_GENTLY },
	{ "backfill", cmd_backfill, RUN_SETUP },
	{ "bisect", cmd_bisect, RUN_SETUP },
	{ "blame", cmd_blame, RUN_SETUP },
	{ "branch", cmd_branch, RUN_SETUP | DELAY_PAGER_CONFIG },
	{ "bugreport", cmd_bugreport, RUN_SETUP_GENTLY },
	{ "bundle", cmd_bundle, RUN_SETUP_GENTLY },
	{ "cat-file", cmd_cat_file, RUN_SETUP },
	{ "check-attr", cmd_check_attr, RUN_SETUP },
	{ "check-ignore", cmd_check_ignore, RUN_SETUP | NEED_WORK_TREE },
	{ "check-mailmap", cmd_check_mailmap, RUN_SETUP },
	{ "check-ref-format", cmd_check_ref_format, NO_PARSEOPT  },
	{ "checkout", cmd_checkout, RUN_SETUP | NEED_WORK_TREE },
	{ "checkout--worker", cmd_checkout__worker,
		RUN_SETUP | NEED_WORK_TREE },
	{ "checkout-index", cmd_checkout_index,
		RUN_SETUP | NEED_WORK_TREE},
	{ "cherry", cmd_cherry, RUN_SETUP },
	{ "cherry-pick", cmd_cherry_pick, RUN_SETUP | NEED_WORK_TREE },
	{ "clean", cmd_clean, RUN_SETUP | NEED_WORK_TREE },
	{ "clone", cmd_clone },
	{ "column", cmd_column, RUN_SETUP_GENTLY },
	{ "commit", cmd_commit, RUN_SETUP | NEED_WORK_TREE },
	{ "commit-graph", cmd_commit_graph, RUN_SETUP },
	{ "commit-tree", cmd_commit_tree, RUN_SETUP },
	{ "config", cmd_config, RUN_SETUP_GENTLY | DELAY_PAGER_CONFIG },
	{ "count-objects", cmd_count_objects, RUN_SETUP },
	{ "credential", cmd_credential, RUN_SETUP_GENTLY | NO_PARSEOPT },
	{ "credential-cache", cmd_credential_cache },
	{ "credential-cache--daemon", cmd_credential_cache_daemon },
	{ "credential-store", cmd_credential_store },
	{ "describe", cmd_describe, RUN_SETUP },
	{ "diagnose", cmd_diagnose, RUN_SETUP_GENTLY },
	{ "diff", cmd_diff, NO_PARSEOPT },
	{ "diff-files", cmd_diff_files, RUN_SETUP | NEED_WORK_TREE | NO_PARSEOPT },
	{ "diff-index", cmd_diff_index, RUN_SETUP | NO_PARSEOPT },
	{ "diff-pairs", cmd_diff_pairs, RUN_SETUP | NO_PARSEOPT },
	{ "diff-tree", cmd_diff_tree, RUN_SETUP | NO_PARSEOPT },
	{ "difftool", cmd_difftool, RUN_SETUP_GENTLY },
	{ "fast-export", cmd_fast_export, RUN_SETUP },
	{ "fast-import", cmd_fast_import, RUN_SETUP | NO_PARSEOPT },
	{ "fetch", cmd_fetch, RUN_SETUP },
	{ "fetch-pack", cmd_fetch_pack, RUN_SETUP | NO_PARSEOPT },
	{ "fmt-merge-msg", cmd_fmt_merge_msg, RUN_SETUP },
	{ "for-each-ref", cmd_for_each_ref, RUN_SETUP },
	{ "for-each-repo", cmd_for_each_repo, RUN_SETUP_GENTLY },
	{ "format-patch", cmd_format_patch, RUN_SETUP },
	{ "format-rev", cmd_format_rev, RUN_SETUP },
	{ "fsck", cmd_fsck, RUN_SETUP },
	{ "fsck-objects", cmd_fsck, RUN_SETUP },
	{ "fsmonitor--daemon", cmd_fsmonitor__daemon, RUN_SETUP },
	{ "gc", cmd_gc, RUN_SETUP },
	{ "get-tar-commit-id", cmd_get_tar_commit_id, NO_PARSEOPT },
	{ "grep", cmd_grep, RUN_SETUP_GENTLY },
	{ "hash-object", cmd_hash_object },
	{ "help", cmd_help },
	{ "history", cmd_history, RUN_SETUP },
	{ "hook", cmd_hook, RUN_SETUP_GENTLY },
	{ "index-pack", cmd_index_pack, RUN_SETUP_GENTLY | NO_PARSEOPT },
	{ "init", cmd_init_db },
	{ "init-db", cmd_init_db },
	{ "interpret-trailers", cmd_interpret_trailers, RUN_SETUP_GENTLY },
	{ "last-modified", cmd_last_modified, RUN_SETUP },
	{ "log", cmd_log, RUN_SETUP },
	{ "ls-files", cmd_ls_files, RUN_SETUP },
	{ "ls-remote", cmd_ls_remote, RUN_SETUP_GENTLY },
	{ "ls-tree", cmd_ls_tree, RUN_SETUP },
	{ "mailinfo", cmd_mailinfo, RUN_SETUP_GENTLY },
	{ "mailsplit", cmd_mailsplit, NO_PARSEOPT },
	{ "maintenance", cmd_maintenance, RUN_SETUP },
	{ "merge", cmd_merge, RUN_SETUP | NEED_WORK_TREE },
	{ "merge-base", cmd_merge_base, RUN_SETUP },
	{ "merge-file", cmd_merge_file, RUN_SETUP_GENTLY },
	{ "merge-index", cmd_merge_index, RUN_SETUP | NO_PARSEOPT },
	{ "merge-ours", cmd_merge_ours, RUN_SETUP | NO_PARSEOPT },
	{ "merge-recursive", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE | NO_PARSEOPT },
	{ "merge-recursive-ours", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE | NO_PARSEOPT },
	{ "merge-recursive-theirs", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE | NO_PARSEOPT },
	{ "merge-subtree", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE | NO_PARSEOPT },
	{ "merge-tree", cmd_merge_tree, RUN_SETUP },
	{ "mktag", cmd_mktag, RUN_SETUP },
	{ "mktree", cmd_mktree, RUN_SETUP },
	{ "multi-pack-index", cmd_multi_pack_index, RUN_SETUP },
	{ "mv", cmd_mv, RUN_SETUP | NEED_WORK_TREE },
	{ "name-rev", cmd_name_rev, RUN_SETUP },
	{ "notes", cmd_notes, RUN_SETUP },
	{ "pack-objects", cmd_pack_objects, RUN_SETUP },
#ifndef WITH_BREAKING_CHANGES
	{ "pack-redundant", cmd_pack_redundant, RUN_SETUP | NO_PARSEOPT | DEPRECATED },
#endif
	{ "pack-refs", cmd_pack_refs, RUN_SETUP },
	{ "patch-id", cmd_patch_id, RUN_SETUP_GENTLY | NO_PARSEOPT },
	{ "pickaxe", cmd_blame, RUN_SETUP },
	{ "prune", cmd_prune, RUN_SETUP },
	{ "prune-packed", cmd_prune_packed, RUN_SETUP },
	{ "pull", cmd_pull, RUN_SETUP | NEED_WORK_TREE },
	{ "push", cmd_push, RUN_SETUP },
	{ "range-diff", cmd_range_diff, RUN_SETUP | USE_PAGER },
	{ "read-tree", cmd_read_tree, RUN_SETUP },
	{ "rebase", cmd_rebase, RUN_SETUP | NEED_WORK_TREE },
	{ "receive-pack", cmd_receive_pack },
	{ "reflog", cmd_reflog, RUN_SETUP },
	{ "refs", cmd_refs, RUN_SETUP },
	{ "remote", cmd_remote, RUN_SETUP },
	{ "remote-ext", cmd_remote_ext, NO_PARSEOPT },
	{ "remote-fd", cmd_remote_fd, NO_PARSEOPT },
	{ "repack", cmd_repack, RUN_SETUP },
	{ "replace", cmd_replace, RUN_SETUP },
	{ "replay", cmd_replay, RUN_SETUP },
	{ "repo", cmd_repo, RUN_SETUP },
	{ "rerere", cmd_rerere, RUN_SETUP },
	{ "reset", cmd_reset, RUN_SETUP },
	{ "restore", cmd_restore, RUN_SETUP | NEED_WORK_TREE },
	{ "rev-list", cmd_rev_list, RUN_SETUP | NO_PARSEOPT },
	{ "rev-parse", cmd_rev_parse, NO_PARSEOPT },
	{ "revert", cmd_revert, RUN_SETUP | NEED_WORK_TREE },
	{ "rm", cmd_rm, RUN_SETUP },
	{ "send-pack", cmd_send_pack, RUN_SETUP },
	{ "shortlog", cmd_shortlog, RUN_SETUP_GENTLY | USE_PAGER },
	{ "show", cmd_show, RUN_SETUP },
	{ "show-branch", cmd_show_branch, RUN_SETUP },
	{ "show-index", cmd_show_index, RUN_SETUP_GENTLY },
	{ "show-ref", cmd_show_ref, RUN_SETUP },
	{ "sparse-checkout", cmd_sparse_checkout, RUN_SETUP },
	{ "stage", cmd_add, RUN_SETUP | NEED_WORK_TREE },
	{ "stash", cmd_stash, RUN_SETUP | NEED_WORK_TREE },
	{ "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
	{ "stripspace", cmd_stripspace },
	{ "submodule--helper", cmd_submodule__helper, RUN_SETUP },
	{ "switch", cmd_switch, RUN_SETUP | NEED_WORK_TREE },
	{ "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
	{ "tag", cmd_tag, RUN_SETUP | DELAY_PAGER_CONFIG },
	{ "unpack-file", cmd_unpack_file, RUN_SETUP | NO_PARSEOPT },
	{ "unpack-objects", cmd_unpack_objects, RUN_SETUP | NO_PARSEOPT },
	{ "update-index", cmd_update_index, RUN_SETUP },
	{ "update-ref", cmd_update_ref, RUN_SETUP },
	{ "update-server-info", cmd_update_server_info, RUN_SETUP },
	{ "upload-archive", cmd_upload_archive, NO_PARSEOPT },
	{ "upload-archive--writer", cmd_upload_archive_writer, NO_PARSEOPT },
	{ "upload-pack", cmd_upload_pack },
	{ "url-parse", cmd_url_parse },
	{ "var", cmd_var, RUN_SETUP_GENTLY | NO_PARSEOPT },
	{ "verify-commit", cmd_verify_commit, RUN_SETUP },
	{ "verify-pack", cmd_verify_pack },
	{ "verify-tag", cmd_verify_tag, RUN_SETUP },
	{ "version", cmd_version },
#ifndef WITH_BREAKING_CHANGES
	{ "whatchanged", cmd_whatchanged, RUN_SETUP | DEPRECATED },
#endif
	{ "worktree", cmd_worktree, RUN_SETUP },
	{ "write-tree", cmd_write_tree, RUN_SETUP },
};

static struct cmd_struct *get_builtin(const char *s)
{
	for (size_t i = 0; i < ARRAY_SIZE(commands); i++) {
		struct cmd_struct *p = commands + i;
		if (!strcmp(s, p->cmd))
			return p;
	}
	return NULL;
}

int is_builtin(const char *s)
{
	return !!get_builtin(s);
}

static void list_builtins(struct string_list *out,
			  unsigned int include_option,
			  unsigned int exclude_option)
{
	if (include_option && exclude_option)
		BUG("'include_option' and 'exclude_option' are mutually exclusive");
	for (size_t i = 0; i < ARRAY_SIZE(commands); i++) {
		if (include_option && !(commands[i].option & include_option))
			continue;
		if (exclude_option && (commands[i].option & exclude_option))
			continue;
		string_list_append(out, commands[i].cmd);
	}
}

void load_builtin_commands(const char *prefix, struct cmdnames *cmds)
{
	const char *name;

	/*
	 * Callers can ask for a subset of the commands based on a certain
	 * prefix, which is then dropped from the added names. The names in
	 * the `commands[]` array do not have the `git-` prefix, though,
	 * therefore we must expect the `prefix` to at least start with `git-`.
	 */
	if (!skip_prefix(prefix, "git-", &prefix))
		BUG("prefix '%s' must start with 'git-'", prefix);

	for (size_t i = 0; i < ARRAY_SIZE(commands); i++)
		if (skip_prefix(commands[i].cmd, prefix, &name))
			add_cmdname(cmds, name, strlen(name));
}

#ifdef STRIP_EXTENSION
static void strip_extension(struct strvec *args)
{
	size_t len;

	if (strip_suffix(args->v[0], STRIP_EXTENSION, &len)) {
		char *stripped = xmemdupz(args->v[0], len);
		strvec_replace(args, 0, stripped);
		free(stripped);
	}
}
#else
#define strip_extension(cmd)
#endif

static void handle_builtin(struct strvec *args)
{
	const char *cmd;
	struct cmd_struct *builtin;

	strip_extension(args);
	cmd = args->v[0];

	/* Turn "git cmd --help" into "git help --exclude-guides cmd" */
	if (args->nr > 1 && !strcmp(args->v[1], "--help")) {
		const char *exclude_guides_arg[] = { "--exclude-guides" };

		strvec_replace(args, 1, args->v[0]);
		strvec_replace(args, 0, "help");
		cmd = "help";
		strvec_splice(args, 2, 0, exclude_guides_arg,
			      ARRAY_SIZE(exclude_guides_arg));
	}

	builtin = get_builtin(cmd);
	if (builtin) {
		const char **argv_copy = NULL;
		int ret;

		/*
		 * `run_builtin()` will modify the argv array, so we need to
		 * create a shallow copy such that we can free all of its
		 * strings.
		 */
		if (args->nr)
			DUP_ARRAY(argv_copy, args->v, args->nr + 1);

		ret = run_builtin(builtin, args->nr, argv_copy, the_repository);
		strvec_clear(args);
		free(argv_copy);
		exit(ret);
	}
}

static void execv_dashed_external(const char **argv)
{
	struct child_process cmd = CHILD_PROCESS_INIT;
	int status;

	if (use_pager == -1 && !is_builtin(argv[0]))
		use_pager = check_pager_config(the_repository, argv[0]);
	commit_pager_choice();

	strvec_pushf(&cmd.args, "git-%s", argv[0]);
	strvec_pushv(&cmd.args, argv + 1);
	cmd.clean_on_exit = 1;
	cmd.wait_after_clean = 1;
	cmd.silent_exec_failure = 1;
	cmd.trace2_child_class = "dashed";

	trace2_cmd_name("_run_dashed_");

	/*
	 * The code in run_command() logs trace2 child_start/child_exit
	 * events, so we do not need to report exec/exec_result events here.
	 */
	trace_argv_printf(cmd.args.v, "trace: exec:");

	/*
	 * If we fail because the command is not found, it is
	 * OK to return. Otherwise, we just pass along the status code,
	 * or our usual generic code if we were not even able to exec
	 * the program.
	 */
	status = run_command(&cmd);

	/*
	 * If the child process ran and we are now going to exit, emit a
	 * generic string as our trace2 command verb to indicate that we
	 * launched a dashed command.
	 */
	if (status >= 0)
		exit(status);
	else if (errno != ENOENT)
		exit(128);
}

static int is_deprecated_command(const char *cmd)
{
	struct cmd_struct *builtin = get_builtin(cmd);
	return builtin && (builtin->option & DEPRECATED);
}

static int run_argv(struct strvec *args)
{
	int done_alias = 0;
	struct string_list expanded_aliases = STRING_LIST_INIT_DUP;

	while (1) {
		/*
		 * Allow deprecated commands to be overridden by aliases. This
		 * creates a seamless path forward for people who want to keep
		 * using the name after it is gone, but want to skip the
		 * deprecation complaint in the meantime.
		 */
		if (is_deprecated_command(args->v[0]) &&
		    handle_alias(args, &expanded_aliases)) {
			done_alias = 1;
			continue;
		}
		/*
		 * If we tried alias and futzed with our environment,
		 * it no longer is safe to invoke builtins directly in
		 * general.  We have to spawn them as dashed externals.
		 *
		 * NEEDSWORK: if we can figure out cases
		 * where it is safe to do, we can avoid spawning a new
		 * process.
		 */
		if (!done_alias)
			handle_builtin(args);
		else if (get_builtin(args->v[0])) {
			struct child_process cmd = CHILD_PROCESS_INIT;
			int err;

			/*
			 * The current process is committed to launching a
			 * child process to run the command named in (**argv)
			 * and exiting.  Log a generic string as the trace2
			 * command verb to indicate this.  Note that the child
			 * process will log the actual verb when it runs.
			 */
			trace2_cmd_name("_run_git_alias_");

			commit_pager_choice();

			strvec_push(&cmd.args, "git");
			strvec_pushv(&cmd.args, args->v);

			trace_argv_printf(cmd.args.v, "trace: exec:");

			/*
			 * if we fail because the command is not found, it is
			 * OK to return. Otherwise, we just pass along the status code.
			 */
			cmd.silent_exec_failure = 1;
			cmd.clean_on_exit = 1;
			cmd.wait_after_clean = 1;
			cmd.trace2_child_class = "git_alias";
			err = run_command(&cmd);
			if (err >= 0 || errno != ENOENT)
				exit(err);
			die("could not execute builtin %s", args->v[0]);
		}

		/* .. then try the external ones */
		execv_dashed_external(args->v);

		/*
		 * It could be an alias -- this works around the insanity
		 * of overriding "git log" with "git show" by having
		 * alias.log = show
		 */
		if (!handle_alias(args, &expanded_aliases))
			break;
		done_alias = 1;
	}

	string_list_clear(&expanded_aliases, 0);

	return done_alias;
}

int cmd_main(int argc, const char **argv)
{
	struct strvec args = STRVEC_INIT;
	const char *cmd;
	int done_help = 0;

	cmd = argv[0];
	if (!cmd)
		cmd = "git-help";
	else {
		const char *slash = find_last_dir_sep(cmd);
		if (slash)
			cmd = slash + 1;
	}

	trace_command_performance(argv);

	/*
	 * "git-xxxx" is the same as "git xxxx", but we obviously:
	 *
	 *  - cannot take flags in between the "git" and the "xxxx".
	 *  - cannot execute it externally (since it would just do
	 *    the same thing over again)
	 *
	 * So we just directly call the builtin handler, and die if
	 * that one cannot handle it.
	 */
	if (skip_prefix(cmd, "git-", &cmd)) {
		strvec_push(&args, cmd);
		strvec_pushv(&args, argv + 1);
		handle_builtin(&args);
		strvec_clear(&args);
		die(_("cannot handle %s as a builtin"), cmd);
	}

	/* Look for flags.. */
	argv++;
	argc--;
	handle_options(&argv, &argc, NULL);

	if (!argc) {
		/* The user didn't specify a command; give them help */
		commit_pager_choice();
		printf(_("usage: %s\n\n"), git_usage_string);
		list_common_cmds_help();
		printf("\n%s\n", _(git_more_info_string));
		exit(1);
	}

	if (!strcmp("--version", argv[0]) || !strcmp("-v", argv[0]))
		argv[0] = "version";
	else if (!strcmp("--help", argv[0]) || !strcmp("-h", argv[0]))
		argv[0] = "help";

	cmd = argv[0];

	/*
	 * We use PATH to find git commands, but we prepend some higher
	 * precedence paths: the "--exec-path" option, the GIT_EXEC_PATH
	 * environment, and the $(gitexecdir) from the Makefile at build
	 * time.
	 */
	setup_path();

	for (int i = 0; i < argc; i++)
		strvec_push(&args, argv[i]);

	while (1) {
		int was_alias = run_argv(&args);
		if (errno != ENOENT)
			break;
		if (was_alias) {
			fprintf(stderr, _("expansion of alias '%s' failed; "
					  "'%s' is not a git command\n"),
				cmd, args.v[0]);
			strvec_clear(&args);
			exit(1);
		}
		if (!done_help) {
			char *assumed = help_unknown_cmd(cmd);
			strvec_replace(&args, 0, assumed);
			free(assumed);
			cmd = args.v[0];
			done_help = 1;
		} else {
			break;
		}
	}

	fprintf(stderr, _("failed to run command '%s': %s\n"),
		cmd, strerror(errno));
	strvec_clear(&args);

	return 1;
}

back to top

Software Heritage — Copyright (C) 2015–2026, The Software Heritage developers. License: GNU AGPLv3+.
The source code of Software Heritage itself is available on our development forge.
The source code files archived by Software Heritage are available under their own copyright and licenses.
Terms of use: Archive access, API— Content policy— Contact— JavaScript license information— Web API