{"course":{"slug":"hugging-face","title":"Hugging Face, End to End","url":"https://addaly.com/learn/hugging-face","level":"intermediate","track":"create"},"lesson":{"slug":"the-hub-is-git","title":"The Hub is git, and that explains everything else","url":"https://addaly.com/learn/hugging-face/the-hub-is-git","position":1,"of":75,"minutes":10,"answer":"A model on the Hub is a git repository with big files stored beside it, so it has versions, history and pull requests; and if you do not pin a revision.","keyIdea":"A model on the Hub is a git repository with big files stored beside it, so it has versions, history and pull requests; and if you do not pin a revision, the model under your code can change without your code changing."},"body":{"format":"markdown","text":"## Most people use the Hub as a download button\n\nOpen a model page (`openai/whisper-large-v3`, say) and look at the tabs across the top: Model card, Files and versions, Community. Then look inside Files and versions. There is a commit history. There are branches. Under Community there are pull requests sitting alongside the discussion threads.\n\nNone of that is decoration. A Hugging Face repository **is** a git repository. The model card is a file called `README.md` inside it. The weights are files committed to it. The Hub is a git host that happens to be full of neural networks, and almost everything confusing about the platform stops being confusing once you hold that one fact.\n\n## What being git actually buys you\n\n**A model has versions, and `main` moves.** When a lab fixes a tokenizer bug or reuploads a corrected checkpoint, the repository gets a new commit. If your code says `from_pretrained(\"some-org/some-model\")`, it means *whatever is on main at the moment it runs*. That is fine while you are experimenting and a real hazard in something you have shipped, because your model can change without your code changing. Every loading function takes a `revision` argument, and it accepts a branch, a tag, or a commit hash:\n\n```python\nfrom transformers import AutoModel\n\nmodel = AutoModel.from_pretrained(\n    \"sentence-transformers/all-MiniLM-L6-v2\",\n    revision=\"PASTE_THE_COMMIT_HASH_FROM_THE_HISTORY_TAB\",\n)\n```\n\n> Figure: Eighteen months on one model's main branch. Timeline: Day 0, First release: weights, tokenizer, and a card with no licence field at all.; Week 2, A contributor's pull request adds the licence tag and a safetensors copy of the weights.; Month 3, The chat template inside tokenizer_config.json is corrected. Every prompt rendered before this date was subtly the wrong shape.; Month 7, A reuploaded checkpoint fixes a tokenizer bug. Same repository, same name, different bytes.; Month 11, The repository is gated retrospectively. Unauthenticated downloads start returning 403 in a pipeline nobody touched.; Month 18, A second contributor adds a stated limitation to the card: three lines that save the next reader a week.. None of these commits changed the model's name. Code that did not pin a revision picked up every one of them, the next time it ran, with no change of its own. The hash in the History tab is what makes a result reproducible.\n\nCopy the hash from the History link in Files and versions. Pinning costs you nothing and removes a whole category of Monday morning.\n\n**You can read the diff.** History shows what changed and when. If a model started behaving differently last week, that is the first place to look, and often the last.\n\n**Anyone can open a pull request.** Someone adds a missing licence tag, converts weights to safetensors, translates a card into Hindi. On the Hub these live as real git refs like `refs/pr/4`, so you can download and test a proposed change before it is merged.\n\n**Nothing has to be public.** A repository can be private, and every command below works the same way with a token.\n\n## The big files are not really in git\n\nGit was built for source code. It handles a 5 GB tensor file badly. So the Hub keeps the *pointers* in git and the *bytes* in a separate large-file store: Git LFS originally, and since 2025 Hugging Face has been moving repositories onto its own chunk-level storage that deduplicates blocks, so reuploading a slightly changed checkpoint does not resend the whole thing. You do not need to know which one sits behind a given repo. You do need to know one consequence.\n\nRun this on a machine that does not have Git LFS installed:\n\n```bash\ngit clone https://huggingface.co/openai/whisper-small\n```\n\nYou get a folder with all the right filenames and a `model.safetensors` that is about 130 bytes. It is not corrupt and the download did not fail. It is the pointer file (a few lines of text naming an object hash) because git faithfully cloned exactly what was committed, and nothing fetched the real object. People lose an hour to this and conclude the model is broken.\n\n## Three ways to actually get the files\n\n**The browser.** Every file has a download link. On a phone this is the only route, and for one 200 MB file it is fine. Direct URLs follow a single pattern worth memorising: `https://huggingface.co/<owner>/<repo>/resolve/main/<filename>`.\n\n**The command line.** Install the client and use it instead of raw git:\n\n```bash\npip install -U huggingface_hub\nhf download openai/whisper-small\n```\n\nThe command was called `huggingface-cli` for years and was renamed to `hf` in 2025. If your shell says `hf: command not found`, upgrade the package, or use the old name: both are common in tutorials you will find. `hf download` resolves large files properly, resumes an interrupted transfer, and puts everything in a shared cache rather than a folder you will forget about. That last part matters more than it sounds; lesson seven is about the disk it fills.\n\n**Python, when you want one file.** A repository often holds three copies of the same weights in different formats. You rarely want all of them:\n\n```python\nfrom huggingface_hub import hf_hub_download\n\npath = hf_hub_download(\"openai/whisper-small\", \"model.safetensors\")\n```\n\nFor a subset, `snapshot_download` takes `allow_patterns`: passing `[\"*.safetensors\", \"*.json\"]` skips the duplicate PyTorch `.bin` files and can halve what you pull down.\n\n## Do this now\n\nPick any model you have used through somebody else's app. Open Files and versions, then History. Count how many times it has changed since it was announced, and read the message on the most recent commit. That commit is the version you have been using."},"check":{"question":"Someone runs `git clone` on a model repository and ends up with a `model.safetensors` of about 130 bytes containing a few lines of text. What has happened?","options":[{"text":"The repository is gated and they were not authenticated, so the server returned a placeholder file instead of the weights.","correct":false,"tempting":"Assumes an access control would hand back a stub file rather than refuse the request outright."},{"text":"The transfer was cut short by the network and needs to be resumed before the file is complete.","correct":false,"tempting":"Reads a consistent, exactly-sized text file as a partial binary download."},{"text":"Git cloned the pointer file that stands in for the weights; the actual bytes live in a large-file store that Git LFS or `hf download` fetches separately.","correct":true},{"text":"The branch `main` had moved to a commit made before the weights were uploaded, so only the metadata existed at that revision.","correct":false,"tempting":"Blames repository versioning for a checkout that reproduced exactly what was committed."}]},"prev":null,"next":{"slug":"searching-the-hub","title":"The search box is the worst way in","url":"https://addaly.com/learn/hugging-face/searching-the-hub"},"authorship":"Founder and series editor: Dr Mustafa Mun. Written by AI models to a written standard. Exam keys checked blind by a second model: 3,665 of 3,666 agreed. Academic review invited.","review":{"status":"Draft, open for academic review"},"cite":"Addaly (2026) 'The Hub is git, and that explains everything else', in Hugging Face, End to End. Edited by M. Mun. Addaly. Available at: https://addaly.com/learn/hugging-face/the-hub-is-git (Accessed: 31 October 2026).","licence":{"name":"CC BY-NC-SA 4.0","url":"https://creativecommons.org/licenses/by-nc-sa/4.0/"},"reader":{"schema":1,"title":"The Hub is git, and that explains everything else","courseTitle":"Hugging Face, End to End","module":{"index":1,"slug":"the-hub-as-a-repository-host","title":"The Hub, read properly"},"answer":null,"keyIdea":"A model on the Hub is a git repository with big files stored beside it, so it has versions, history and pull requests; and if you do not pin a revision, the model under your code can change without your code changing.","hero":{"kind":"figure","figure":{"t":"figure","label":"Figure 1.1","figure":{"type":"timeline","title":"Eighteen months on one model’s main branch","events":[{"when":"Day 0","what":"First release: weights, tokenizer, and a card with no licence field at all."},{"when":"Week 2","what":"A contributor’s pull request adds the licence tag and a safetensors copy of the weights."},{"when":"Month 3","what":"The chat template inside tokenizer_config.json is corrected. Every prompt rendered before this date was subtly the wrong shape."},{"when":"Month 7","what":"A reuploaded checkpoint fixes a tokenizer bug. Same repository, same name, different bytes."},{"when":"Month 11","what":"The repository is gated retrospectively. Unauthenticated downloads start returning 403 in a pipeline nobody touched."},{"when":"Month 18","what":"A second contributor adds a stated limitation to the card: three lines that save the next reader a week."}],"caption":"None of these commits changed the model’s name. Code that did not pin a revision picked up every one of them, the next time it ran, with no change of its own. The hash in the History tab is what makes a result reproducible."},"alt":"Figure: Eighteen months on one model’s main branch. Timeline: Day 0, First release: weights, tokenizer, and a card with no licence field at all.; Week 2, A contributor’s pull request adds the licence tag and a safetensors copy of the weights.; Month 3, The chat template inside tokenizer_config.json is corrected. Every prompt rendered before this date was subtly the wrong shape.; Month 7, A reuploaded checkpoint fixes a tokenizer bug. Same repository, same name, different bytes.; Month 11, The repository is gated retrospectively. Unauthenticated downloads start returning 403 in a pipeline nobody touched.; Month 18, A second contributor adds a stated limitation to the card: three lines that save the next reader a week.. None of these commits changed the model’s name. Code that did not pin a revision picked up every one of them, the next time it ran, with no change of its own. The hash in the History tab is what makes a result reproducible."}},"blocks":[{"t":"h2","id":"most-people-use-the-hub-as-a-download-button","runs":[{"text":"Most people use the Hub as a download button"}]},{"t":"p","runs":[{"text":"Open a model page ("},{"text":"openai/whisper-large-v3","code":true},{"text":", say) and look at the tabs across the top: Model card, Files and versions, Community. Then look inside Files and versions. There is a commit history. There are branches. Under Community there are pull requests sitting alongside the discussion threads."}]},{"t":"p","runs":[{"text":"None of that is decoration. A Hugging Face repository "},{"text":"is","b":true},{"text":" a git repository. The model card is a file called "},{"text":"README.md","code":true},{"text":" inside it. The weights are files committed to it. The Hub is a git host that happens to be full of neural networks, and almost everything confusing about the platform stops being confusing once you hold that one fact."}]},{"t":"h2","id":"what-being-git-actually-buys-you","runs":[{"text":"What being git actually buys you"}]},{"t":"p","runs":[{"text":"A model has versions, and ","b":true},{"text":"main","b":true,"code":true},{"text":" moves.","b":true},{"text":" When a lab fixes a tokenizer bug or reuploads a corrected checkpoint, the repository gets a new commit. If your code says "},{"text":"from_pretrained(\"some-org/some-model\")","code":true},{"text":", it means "},{"text":"whatever is on main at the moment it runs","i":true},{"text":". That is fine while you are experimenting and a real hazard in something you have shipped, because your model can change without your code changing. Every loading function takes a "},{"text":"revision","code":true},{"text":" argument, and it accepts a branch, a tag, or a commit hash:"}]},{"t":"code","lang":"python","label":"Python","text":"from transformers import AutoModel\n\nmodel = AutoModel.from_pretrained(\n    \"sentence-transformers/all-MiniLM-L6-v2\",\n    revision=\"PASTE_THE_COMMIT_HASH_FROM_THE_HISTORY_TAB\",\n)"},{"t":"p","runs":[{"text":"Copy the hash from the History link in Files and versions. Pinning costs you nothing and removes a whole category of Monday morning."}]},{"t":"p","runs":[{"text":"You can read the diff.","b":true},{"text":" History shows what changed and when. If a model started behaving differently last week, that is the first place to look, and often the last."}]},{"t":"p","runs":[{"text":"Anyone can open a pull request.","b":true},{"text":" Someone adds a missing licence tag, converts weights to safetensors, translates a card into Hindi. On the Hub these live as real git refs like "},{"text":"refs/pr/4","code":true},{"text":", so you can download and test a proposed change before it is merged."}]},{"t":"p","runs":[{"text":"Nothing has to be public.","b":true},{"text":" A repository can be private, and every command below works the same way with a token."}]},{"t":"h2","id":"the-big-files-are-not-really-in-git","runs":[{"text":"The big files are not really in git"}]},{"t":"p","runs":[{"text":"Git was built for source code. It handles a 5 GB tensor file badly. So the Hub keeps the "},{"text":"pointers","i":true},{"text":" in git and the "},{"text":"bytes","i":true},{"text":" in a separate large-file store: Git LFS originally, and since 2025 Hugging Face has been moving repositories onto its own chunk-level storage that deduplicates blocks, so reuploading a slightly changed checkpoint does not resend the whole thing. You do not need to know which one sits behind a given repo. You do need to know one consequence."}]},{"t":"p","runs":[{"text":"Run this on a machine that does not have Git LFS installed:"}]},{"t":"code","lang":"bash","label":"Bash","text":"git clone https://huggingface.co/openai/whisper-small"},{"t":"p","runs":[{"text":"You get a folder with all the right filenames and a "},{"text":"model.safetensors","code":true},{"text":" that is about 130 bytes. It is not corrupt and the download did not fail. It is the pointer file (a few lines of text naming an object hash) because git faithfully cloned exactly what was committed, and nothing fetched the real object. People lose an hour to this and conclude the model is broken."}]},{"t":"h2","id":"three-ways-to-actually-get-the-files","runs":[{"text":"Three ways to actually get the files"}]},{"t":"p","runs":[{"text":"The browser.","b":true},{"text":" Every file has a download link. On a phone this is the only route, and for one 200 MB file it is fine. Direct URLs follow a single pattern worth memorising: "},{"text":"https://huggingface.co/<owner>/<repo>/resolve/main/<filename>","code":true},{"text":"."}]},{"t":"p","runs":[{"text":"The command line.","b":true},{"text":" Install the client and use it instead of raw git:"}]},{"t":"code","lang":"bash","label":"Bash","text":"pip install -U huggingface_hub\nhf download openai/whisper-small"},{"t":"p","runs":[{"text":"The command was called "},{"text":"huggingface-cli","code":true},{"text":" for years and was renamed to "},{"text":"hf","code":true},{"text":" in 2025. If your shell says "},{"text":"hf: command not found","code":true},{"text":", upgrade the package, or use the old name: both are common in tutorials you will find. "},{"text":"hf download","code":true},{"text":" resolves large files properly, resumes an interrupted transfer, and puts everything in a shared cache rather than a folder you will forget about. That last part matters more than it sounds; lesson seven is about the disk it fills."}]},{"t":"p","runs":[{"text":"Python, when you want one file.","b":true},{"text":" A repository often holds three copies of the same weights in different formats. You rarely want all of them:"}]},{"t":"code","lang":"python","label":"Python","text":"from huggingface_hub import hf_hub_download\n\npath = hf_hub_download(\"openai/whisper-small\", \"model.safetensors\")"},{"t":"p","runs":[{"text":"For a subset, "},{"text":"snapshot_download","code":true},{"text":" takes "},{"text":"allow_patterns","code":true},{"text":": passing "},{"text":"[\"*.safetensors\", \"*.json\"]","code":true},{"text":" skips the duplicate PyTorch "},{"text":".bin","code":true},{"text":" files and can halve what you pull down."}]},{"t":"h2","id":"do-this-now","runs":[{"text":"Do this now"}],"role":"try"},{"t":"p","runs":[{"text":"Pick any model you have used through somebody else’s app. Open Files and versions, then History. Count how many times it has changed since it was announced, and read the message on the most recent commit. That commit is the version you have been using."}]}],"check":{"question":[{"text":"Someone runs "},{"text":"git clone","code":true},{"text":" on a model repository and ends up with a "},{"text":"model.safetensors","code":true},{"text":" of about 130 bytes containing a few lines of text. What has happened?"}],"options":[{"text":[{"text":"The repository is gated and they were not authenticated, so the server returned a placeholder file instead of the weights."}],"correct":false,"tempting":[{"text":"Assumes an access control would hand back a stub file rather than refuse the request outright."}]},{"text":[{"text":"The transfer was cut short by the network and needs to be resumed before the file is complete."}],"correct":false,"tempting":[{"text":"Reads a consistent, exactly-sized text file as a partial binary download."}]},{"text":[{"text":"Git cloned the pointer file that stands in for the weights; the actual bytes live in a large-file store that Git LFS or "},{"text":"hf download","code":true},{"text":" fetches separately."}],"correct":true,"tempting":null},{"text":[{"text":"The branch "},{"text":"main","code":true},{"text":" had moved to a commit made before the weights were uploaded, so only the metadata existed at that revision."}],"correct":false,"tempting":[{"text":"Blames repository versioning for a checkout that reproduced exactly what was committed."}]}],"order":[0,2,1,3],"why":null},"prev":null,"next":{"slug":"searching-the-hub","title":"The search box is the worst way in","minutes":9}}}