)]}'
{
  "commit": "4fe87617ff4ea1726ef43d072e7ef4f0178fa663",
  "tree": "9010fed126bf1b27f62483d93c2f79dd4a3d182a",
  "parents": [
    "d7299422ae593e4b53a7f1e9526a66512039ece1"
  ],
  "author": {
    "name": "James Hawkins",
    "email": "jhawkins@google.com",
    "time": "Fri Aug 28 21:06:05 2026 +0000"
  },
  "committer": {
    "name": "gerrit-scoped@luci-project-accounts.iam.gserviceaccount.com",
    "email": "gerrit-scoped@luci-project-accounts.iam.gserviceaccount.com",
    "time": "Fri Aug 28 16:03:06 2026 -0700"
  },
  "message": "project: read HEAD directly in-memory to avoid subprocesses\n\nBenchmark:\n* repo upload frameworks/base: 2.84s -\u003e 0.50s (-82.3%, 5.6x faster)\n* repo upload (3,045 projects): 7.66s -\u003e 5.24s (-31.6%, 2.42s saved)\n\nFrom repo\u0027s inception through v2.56, GetHead() directly read the\n`.git/HEAD` file in Python. In commit 52bab0ba (\"project: Use git\nrev-parse to read HEAD\"), this was replaced with git subprocess calls\non the premise that git provides a dedicated command. However, in\nlarge multi-project workspaces (such as Android with 3,000+ projects),\nspawning thousands of git processes introduced severe latency\nregressions during `repo upload` and `repo status`.\n\nFurthermore, switching to subprocesses broke detached HEADs and\nunborn branches (fixed in commits 7f7d70ef and 8c3585f3 by re-adding\nthe v2.56 file-reading logic as an error recovery fallback).\n\nThis patch restores fast in-memory reading as the primary path, while\nadding modern defensive safeguards:\n* Symbolic refs (`ref: refs/heads/...`): Strips whitespace and tabs\n  and returns the ref directly in memory.\n* Detached HEAD: Validates 40-char SHA-1 and 64-char SHA-256 commit\n  hashes via git_config.IsId(), normalizing to lowercase.\n* Symlinks: Detects filesystem symlinks via os.path.islink() and\n  safely falls back to git symbolic-ref.\n* Fallback: Catches (OSError, AssertionError) and transparently falls\n  back to native git commands for reftables, unexpected layouts, or\n  filesystem errors. Unifies recovery fallback parsing with the fast\n  path (CRLF/tabs, lowercase hashes, and consistent RelPath errors).\n\nIn addition, this change substantially expands test coverage in\ntests/test_project.py, adding comprehensive unit tests for symbolic\nrefs, whitespace/tabs, CRLF line endings, SHA-1, SHA-256, uppercase\nhash normalization, symlinks, corrupted worktrees, and fallback\nrobustness.\n\nTest: ./run_tests tests/test_project.py\nChange-Id: Ib5c2530117c6939e4b9293feda81aa745c003c6a\nReviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/623001\nCommit-Queue: James Hawkins \u003cjhawkins@google.com\u003e\nReviewed-by: Brian Gan \u003cbrgan@google.com\u003e\nTested-by: James Hawkins \u003cjhawkins@google.com\u003e\n",
  "tree_diff": [
    {
      "type": "modify",
      "old_id": "a73c7d4fa1b9f255a1ba55cdc0115230fe8028b0",
      "old_mode": 33188,
      "old_path": "project.py",
      "new_id": "45a3aa865fa9f2c0174cffa68b30dbaac64ef80b",
      "new_mode": 33188,
      "new_path": "project.py"
    },
    {
      "type": "modify",
      "old_id": "e716b1a7328bb666303979a382c568455ad4948d",
      "old_mode": 33188,
      "old_path": "tests/test_project.py",
      "new_id": "cd184f84d09b1d0d976a68cc5c6da6c96c2bd171",
      "new_mode": 33188,
      "new_path": "tests/test_project.py"
    }
  ]
}
