bbbd8958d58ef4d08410683f00b476095fda53cc
hiram
  Fri Jun 12 16:05:30 2026 -0700
improved source tree update refs #31811

diff --git src/hg/utils/otto/userRequests/ottoLib.py src/hg/utils/otto/userRequests/ottoLib.py
index bed95f72d69..a36a6dcda8a 100644
--- src/hg/utils/otto/userRequests/ottoLib.py
+++ src/hg/utils/otto/userRequests/ottoLib.py
@@ -66,45 +66,58 @@
 
 def gitPullKentTree():
     """Run 'git -C <kentTree> pull' so make commands run against an
     up-to-date checkout (mirrors the first thing chainNetTrackDb.pl
     does after sanity-checking $kentTree).  Returns True on success,
     False otherwise (with the error printed to stderr).  Untracked
     files such as the regenerated tsv.otto are tolerated; conflicting
     local edits will cause 'git pull' to fail, which is what we want
     -- we don't want to silently run makes against a dirty tree.
     The "Already up to date." case is suppressed to keep cron output
     quiet; any other pull output is surfaced to stderr."""
     if not os.path.isdir(os.path.join(kentTree, ".git")):
         print("ERROR: not a git working tree: %s" % kentTree,
               file=sys.stderr)
         return False
+    # Fetch updates from origin
     result = subprocess.run(
-        ["git", "-C", kentTree, "pull"],
-        capture_output=True, text=True,
+        ["git", "-C", kentTree, "fetch", "--prune", "origin"],
+        capture_output=True,
+        text=True,
     )
     if result.returncode != 0:
-        print("ERROR: 'git pull' failed in %s:\n%s%s"
+        print("ERROR: 'git fetch' failed in %s:\n%s%s"
             % (kentTree, result.stdout, result.stderr),
             file=sys.stderr)
         return False
+    # Hard reset working tree to match origin/master
+    result = subprocess.run(
+        ["git", "-C", kentTree, "reset", "--hard", "origin/master"],
+        capture_output=True,
+        text=True,
+    )
+    if result.returncode != 0:
+        print("ERROR: 'git reset --hard' failed in %s:\n%s%s"
+            % (kentTree, result.stdout, result.stderr),
+            file=sys.stderr)
+        return False
+
     out = result.stdout.strip()
     if out and out != "Already up to date.":
         print("# git pull in %s:\n%s" % (kentTree, out), file=sys.stderr)
     return True
 
-
 def hgsql(query, db="hgcentral"):
     """Run hgsql -N -B and return rows as list of tuples (tab-split)."""
     out = subprocess.run(
         ["/cluster/bin/x86_64/hgsql", '-h', 'genome-centdb', "-N", "-B", "-e", query, db],
         check=True, capture_output=True, text=True,
     ).stdout
     return [tuple(line.split("\t")) for line in out.splitlines() if line]
 
 
 def loadDbDbClades():
     """Read dbDb.name.clade.tsv -> {dbName: clade}."""
     result = {}
     with open(cladeTsv) as fh:
         for line in fh:
             if line.startswith("#") or not line.strip():