

- Description
- Livre de comptes ancien ouvert, aux colonnes tracées à la main et aux écritures manuscrites.
- Source
- Daderot · original page
- Licence
- CC0 Public domain
TFVC in depth
Understand the Team Foundation Version Control model: central server, workspaces, numbered changesets.
In brief
- Understand the Team Foundation Version Control model: central server, workspaces, numbered changesets.
- Master the objects: shelvesets, labels, branches, merges, check-in policies, gated check-in.
- Use
tf.execonfidently from the command line (get,add,checkout,checkin,undo,delete,history,merge,branch,status,workfold) and know the pitfalls (TF203069and similar). - Know TFVC’s limits, performance, permissions, and long-term status (as of 2026-10).
On this page
Level: 🟢→🔴
Principles#
🟢 Beginner level
TFVC is a centralized VCS: the server (project collection in Azure DevOps Server or Azure DevOps Services organization) holds the history. Each record is a changeset, numbered sequentially across the entire collection (changeset 45815, for example). The workstation only contains requested files (get) in a workspace: a server folder name ↔ local folder mapping.
Basic cycle:
- Get: retrieve the latest version.
- Checkout (edit): indicate that you are modifying (explicit in server workspace, implicit in local workspace).
- Modify the files.
- Check-in: commit; creates an atomic changeset, with comment and associated work items.
Three strengths: readable changeset numbers, fine-grained folder permissions, and native Visual Studio/work item integration. Three limitations: server dependency, conceptually heavy branching (a branch is a folder), and weaker review/CI tooling than Git.
Server and local workspaces#
🟡 Practical level
| Aspect | Server workspace | Local workspace (modern default) |
|---|---|---|
| Where state is known | Server: it knows which files you have | Workstation: local comparison on demand |
| Read-only files | Yes, until checkout | No: files are editable, changes detected by comparison with cached copy |
| Offline | Limited: Visual Studio “offline” mode, then reconnect later | Possible: tf reconcile afterward |
| Performance for very large repositories (millions of files) | Better | Degrades around 100,000 items (disk scanning, multiple copies) |
| Locking | lock:checkout possible |
No locking checkout on edit |
Choice: a local workspace fits most teams; server workspace remains relevant for massive trees or to enforce locking. Workspaces are per machine and per user (machine name + owner).
tf workspace /new MonWS /collection:https://tfs.exemple.local/tfs/Collection /login:... /noprompt
tf workfold /map $/Projet/Main D:\TFS\Main /workspace:MonWS
tf workspaces # liste
tf workspace /delete MonWS;ProprioXX
A workspace is mapped to one local path; a local folder can belong to only one workspace. Symptom: “The path is already mapped in workspace…” (TF400... / TF203...). Fix: tf workspaces, then delete or unmap the old one (tf workfold /unmap).
Changesets#
🟢 Beginner level
A changeset is the atomic set: added/modified/deleted/renamed/merged files, comment, linked work items, policy results. It is immutable (the comment can be edited later, not the content). Specifics:
- Globally increasing number across the collection: natural reference point to date and say “this fix is in CS 45643.”
- Versioning of renames and deletions: TFVC explicitly records operations; history follows a moved file.
- Version specifiers:
C45643(changeset),D2026-09-16(date),L<label>(label),T(latest),W(workspace).
A changeset can be linked to multiple work items; each then keeps a link to the same changeset.
In a delivery report, cite changesets with explicit text (“CS 45643 – PREBUILD fix”): a raw link is opaque, and this number is what ties together code, work item, build, and package.
Shelvesets#
🟡 Practical level
A shelveset sets aside modifications on the server, named, without committing them. Uses: intermediate backup, sharing for review, context switching, trial build (“shelveset” builds).
tf shelve MonShelveset /comment:"WIP KPI" /noprompt
tf shelvesets /owner:* # lister
tf unshelve MonShelveset;Proprio # restaurer dans le workspace
tf shelve /delete MonShelveset
Differences from git stash: a shelveset is named, server-side, shareable, visible in tools, while stash is local and anonymous. It does not carry a merge graph: it is a snapshot of modifications.
A shelveset is not saved by the workspace: it is saved with the collection database. But it accumulates; without a cleanup policy (deleting old ones), the database grows. Shelvesets containing secrets are readable by users with read permission.
Labels#
🟡 Practical level
A label names a set of file versions (not necessarily consistent: versions can be mixed). It is used to mark a delivery, but it is mutable: you can move, add, or remove items from a label (and unless permissions are properly set, delete it). Contrast with a Git tag, which is a pointer to an atomic commit.
tf label Livraison_9.1.0 $/Projet/Releases/9.1 /recursive /comment:"Kit 9.1.0"
tf labels /collection:...
tf label /delete Livraison_9.1.0@$/Projet
For a delivery, prefer the changeset number (immutable) + a dedicated release branch, rather than label alone. If you use a label, document the corresponding changeset in the delivery documentation.
Branches and merges#
🟡 Practical level
In TFVC, a branch is a server folder declared as a branch (versioned object): $/Projet/Main → $/Projet/Dev, $/Projet/Releases/9.1. Creating a branch logically copies the tree (server-side, expensive only in metadata). The parent/child relationship is recorded: by default, merges are allowed only according to this relationship (downstream/upstream).
tf branch $/Projet/Main $/Projet/Dev /collection:... /comment:"Branche de dev"
tf merge $/Projet/Main $/Projet/Dev /recursive # Main -> Dev (réintégration descendante)
tf merge $/Projet/Dev $/Projet/Main /recursive /version:C45000~C45600 # sélectif par plage de changesets
tf checkin /comment:"Merge Main vers Dev" /noprompt
tf resolve /auto:AutoMerge # résolution automatique
tf resolve fichier /auto:TakeTheirs # TakeTheirs / KeepYours
tf merges $/Projet/Main $/Projet/Dev /recursive # ce qui reste à fusionner
tf history $/Projet/Dev /recursive /format:brief
The server computes the merge of two versions with their common base (following branch relationship). TFVC merges track changesets: you can merge only changeset 45643, which is powerful (surgical backporting) and dangerous (inconsistent order of partially merged changesets).
Baseless merge: merge between two folders without a declared branch relationship (typically a folder copied instead of branched). /baseless option: without an ancestor, everything is a potential conflict because the tool has no comparison base. Reserve for import or reconstruction scenarios.
Classic structure (standard Main/Dev/Release):
$/Projet
├─ Main (stable code, integration)
├─ Dev
│ ├─ Feature-A (feature branches)
│ └─ Feature-B
└─ Releases
├─ 9.0 (release / maintenance branches)
└─ 9.1
Flow rule: merges go from child to parent (reverse integration) and from parent to child (forward integration); you do not merge “sideways” (Release 9.0 → Release 9.1) except via Main, unless baseless. See perspective in 05.
A branch made by folder copy (right-click “copy” instead of “Branch”) creates a folder without branch history: subsequent merges will be baseless. Symptom: everything conflicts. Fix: convert to branch (Convert to Branch) as long as no divergence has occurred, or recreate properly.
Check-in policies and gated check-in#
🔴 Expert level
Check-in policies: rules evaluated on the client side at check-in time (Visual Studio): mandatory work item association, non-empty comment, clean build, code analysis, check-in notes. They are enabled per team project. Structural limit: they run on the client, so they can be bypassed (/override with logged justification).
Gated check-in: check-in is accepted only if a validating build passes. The server creates a shelveset, launches the build, and commits only on success. Useful on a shared trunk: guarantees that Main compiles. Cost: latency, build queueing, contention.
Recent evolution: Microsoft announced in April 2025 the deprecation of old check-in policies (legacy storage and implementation). The timeline includes notification phases then blocking: at the final stage, non-migrated obsolete policies can block all check-ins in the repository. In February 2026, the Azure DevOps team post indicated phase II with a call to remove or replace obsolete policies. Action required: audit policies for each TFVC project, remove obsolete ones, recreate useful ones in the new format (status 2026-10; details and dates to verify on the Azure DevOps blog).
Symptom: policy non-compliance message at every check-in, invisible in tf.exe. Cause: obsolete policies stored on the server. Fix: remove them via Team Explorer or the project admin interface before the final blocking phase.
tf.exe command line#
🟡 Practical level
tf.exe ships with Visual Studio (Common7\IDE\CommonExtensions\Microsoft\TeamFoundation\Team Explorer\TF.exe). It works with integrated Windows authentication or with /login:. In a mapped folder, the collection is inferred from the workspace.
| Command | Role | Example |
|---|---|---|
tf get |
Retrieve versions | tf get $/Projet/Main /recursive ; /force to re-download ; /version:C45643 ; /preview simulates |
tf add |
Add to source control | tf add *.sql /recursive ; /noprompt ; /exclude does not exist: use .tfignore |
tf checkout (tf edit) |
Mark for edit | tf checkout script.sql ; /lock:none |
tf checkin |
Check in | tf checkin /comment:"Message" /associate:12345 /noprompt ; /override:"reason" |
tf undo |
Undo pending changes | tf undo /recursive * ; tf undo /workspace:MonWS;Proprio * (undoes in another workspace) |
tf delete |
Delete | tf delete fichier.sql ; /recursive ; /lock:none |
tf rename / tf move |
Rename/move | tf rename ancien.sql nouveau.sql |
tf status |
Pending changes | tf status /recursive /format:detailed |
tf history |
History | tf history $/Projet/Main/dossier /recursive /stopafter:20 /format:detailed |
tf diff |
Compare | tf diff fichier.sql /version:C45000~T |
tf changeset |
Changeset details | tf changeset 45643 /noprompt |
tf branch / tf merge / tf merges |
Branches, merges | see above |
tf workfold |
Workspace mappings | tf workfold ; /map ; /unmap |
tf workspace / tf workspaces |
Manage workspaces | /new, /delete |
tf shelve / tf unshelve |
Shelvesets | see above |
tf label |
Labels | see above |
tf reconcile |
Reconcile offline changes | tf reconcile /promote /recursive |
tf rollback |
Roll back a changeset | tf rollback /changeset:45643 /recursive |
tf destroy |
Permanently destroy | tf destroy $/Projet/.../x (irreversible, admin rights) |
Typical command-line workflow:
Set-Location D:\TFS
tf get . /recursive # synchronize
tf status . /recursive # check before work
# ... modifications ...
tf add "Datamart\nouveau script.sql" # new files
tf checkin . /recursive /comment:"KPI coverage fix" /associate:12345 /noprompt
In an archival script, read the exit code: 0 success, 1 partial success, 2 unrecognized command, 100 no success. A tf checkin with “nothing to check in” can return a non-zero code without being an error: test tf status first. Protect paths containing spaces with quotes.
tf add applies .tfignore rules when the argument contains a wildcard character (/noignore overrides them), but tf add . /recursive on a folder containing bin/, obj/, and non-ignored caches will add noise. Check tf status before checkin: it's the last barrier.
Find who modified a consolidation script since the last delivery: tf history "$/Projet/Main/Datamart/script.sql" /version:C45000~T /format:detailed. For bulk audits, tf history ... /format:detailed /noprompt redirected to a file, then PowerShell analysis.
.tfignore exclusions#
🟡 Practical level
.tfignore is TFVC's counterpart to .gitignore: a text file at the root of a mapped folder, used by tf add and by Visual Studio (the change detector no longer suggests ignored files). Similar syntax but \ separator and a path starting with \ = relative to the .tfignore folder ; ! to re-include.
# Exclusions
*.user
*.suo
\bin
\obj
\node_modules
!\bin\keep.dll # exception: force add
Rules to know:
.tfignorehas no effect on an item already under source control. To remove a file from source control:tf delete(then check in), the file stays on disk if it was deleted from version only via local workspace ; note thattf deletealso deletes locally by default.- If a change is already pending on the item, run
tf undofirst, otherwise TF203069 (see pitfalls). tf deleterefuses an item never fetched into the workspace (“not found in your workspace”): first do atf getof the item.- The
.tfignoremust itself be versioned so the whole team has the same exclusions ; in a repo that is also tracked by Git locally, keep two aligned lists (.gitignoreand.tfignore).
Work item integration#
🟡 Practical level
TFVC natively links changesets and work items: on save, /associate:<id> (or the Visual Studio panel) creates the Changeset link on the work item, and optional resolution (/resolve:) can change its state. Benefits: from the task you can find the code (and vice versa), builds surface included changesets, and delivery auditing is done by query. With branches, the “Development” tab shows changeset propagation between branches.
Enforce the “associated work item” policy: without it, traceability is lost within a few weeks. A changeset can include multiple tasks, but avoid catch-all commits: one changeset per topic (see also Azure DevOps / TFS in Depth).
Permissions and security#
🔴 Expert level
TFVC permissions are defined by server path (inheritance, explicit deny): Read, Check-out, Check-in, Label, Merge, Branch management, Lock, Permission management, label management, check in changes by other users… This granularity is greater than Git (configured at the repo level). Use case: a subcontractor sees only $/Projet/Releases/9.1/Docs, production can only read Releases.
Principles: permissions by groups not individuals ; reserve explicit deny for exceptions (a deny takes precedence) ; audit periodically with tf permission /collection:... $/Projet /recursive.
Performance and limits#
🔴 Expert level
- Strengths: performs well on very large trees (many files), natural partial loading (only mapped folders), large binaries handled without extensions.
- Weaknesses: slow
statusin local workspace on hundreds of thousands of files ; heavy merge operations on large directory trees ; dependency on network/server ; a collection database that grows with shelvesets and binaries. - Limits: local paths limited by Windows (260 characters) ; less rich code review (Visual Studio code review requests, without an equivalent to PR policies) ; limited offline work (local workspace and
tf reconcile), with no history on the machine. - Mitigations: minimal mapping (
workfold), short root folders, regular cleanup of inactive shelvesets/workspaces, monitor collection database size.
TFVC ratings /5 (context: team of 5 to 30 developers, on-prem Azure DevOps Server, mixed SQL/BI/scripts project):
| Criterion | Rating | Justification |
|---|---|---|
| Learning curve | 4/5 | Model close to SVN, Visual Studio guides |
| Performance on large repositories | 4/5 | Partial mapping, very large trees handled well |
| Branches / merges | 3/5 | Functional, rigid, painful baseless merges |
| Traceability / audit | 4/5 | Numbered changesets, work items |
| Permission granularity | 5/5 | By path, inheritance/deny |
| Binary / large files | 4/5 | No workaround required |
| CI/CD integration | 3/5 | Builds OK, but the modern ecosystem targets Git |
| SQL/BI fit | 3/5 | Text diffs OK ; PBIP/TMDL without PR-based review |
| Ecosystem longevity | 2/5 | Maintained but “feature complete” (see below) |
Pitfalls and error codes#
🔴 Expert level
| Code / symptom | Cause | Remedy |
|---|---|---|
| TF203069 — pending change on item | Attempt to delete/rename/add while a change is already pending |
tf undo the item, then retry the operation |
“not found in your workspace” (tf delete) |
Item was never retrieved locally | tf get the item, then tf delete |
| TF14061 — workspace not found | Workspace deleted or different owner | tf workspaces /computer:*, recreate or map |
| TF10121 — invalid path | Server path written incorrectly ($/ missing) |
Check with tf dir $/ |
| TF400xxx — version conflict | File changed by someone else since your get |
tf get then tf resolve |
| Massive conflicts after folder copy | Baseless merge | Rebuild the branch properly |
| Check-in fails on policies | Obsolete policies (2025-26) | Migrate them, see policies section |
tf checkin “nothing to check in” |
Changes not detected (local workspace, read-only file) | tf reconcile, check tf status |
| “Ghost” files when adding | No .tfignore |
Add .tfignore before the first add |
| Mojibake on accented characters | Files without BOM read as ANSI, or vice versa | See 06 |
TFxxxxxx messages vary by version: check the full text and the corresponding Microsoft help instead of relying on the code alone. I have not verified every number for all versions: this section is guidance, to be confirmed if in doubt.
Product longevity and status#
🔴 Expert level
Status verified through research (sources: Microsoft Learn documentation “Choosing the right version control for your project,” Azure DevOps roadmap posts, Azure DevOps blog):
- Git is the default version control for new Azure DevOps projects; Microsoft recommends Git and considering migration of existing TFVC projects.
- TFVC is “feature complete”: Microsoft maintains compatibility (it remains supported in Azure DevOps and Azure DevOps Server), but all future investment is going to Git. No new features expected.
- The roadmap announced a gradual phase-out of TFVC for new projects and organizations with no existing usage, with a setting to disable TFVC repository creation. Existing TFVC repositories remain usable.
- Legacy check-in policies are being deprecated (2025-2026 timeline, see above).
- Visual Studio: TFVC integration is maintained, but effort is focused on Git.
- To verify at every upgrade: the installed Azure DevOps Server version and its release notes; support policy (“Modern Lifecycle” model); any announcement ending TFVC repository creation for your collection.
Expert reading: TFVC is not removed; it is frozen. An organization using it in production has no technical emergency, but does have a strategic risk: skills debt, lack of innovation (no pull requests, no native AI review capabilities comparable to Git), and risk that third-party tools drop support. The realistic migration timeline is driven by business needs (reviews, CI, hiring), not an imposed date. The approach is detailed in 04.
AI contribution#
With TFVC, AI's contribution is real but weaker than with Git.
| Use case | Benefit | Risk | Maturity | Guardrails |
|---|---|---|---|---|
Write/explain a tf.exe command or a PowerShell archival script |
4/5 | 3/5 | 3/5 | Test with /preview, review paths |
Analyze a tf history export for an audit |
4/5 | 2/5 | 4/5 | Validate figures by recounting |
| Draft check-in comments | 3/5 | 2/5 | 4/5 | Link to a real work item |
| Assistants in Visual Studio (Copilot) for code and messages | 3/5 | 2/5 | 3/5 | AI PR/review features mainly target Git |
Explain a TFxxxxxx error code |
3/5 | 4/5 | 2/5 | AI readily hallucinates numbers: verify in Microsoft docs |
AI does not replace knowledge of the server model (workspaces, mappings) or the decision to archive. See AI in Planning and Version Control Tools.
See also#
- Overview of Version Control Systems · Git in Depth · Git vs TFVC
- Branching Strategies · VCS and SQL / BI data
- Azure DevOps Server (ex-TFS) in Depth · CI/CD
- Matrix: Version Control and Branches · Glossary
✻ Written by Claude Sonnet 5.5