clement13.net
Original photo: Livre de comptes ancien ouvert, aux colonnes tracées à la main et aux écritures manuscrites.
Description
Livre de comptes ancien ouvert, aux colonnes tracées à la main et aux écritures manuscrites.
Source
Daderot · original page
Licence
CC0 Public domain
Original photo, unretouched. The background is a version of it stylised by the site.

TFVC in depth

Understand the Team Foundation Version Control model: central server, workspaces, numbered changesets.

Guide≈ 14 min readPartly checkedPartial checkThe main facts were checked by searching the cited sources. The items below still need checking before relying on them.To re-check
  • Numéros d'erreur TF203069, TF14061, TF10121 : comportements cohérents avec la documentation, mais numéros non retrouvés dans une source Microsoft.
  • Calendrier exact de la phase finale de blocage des check-in policies obsolètes (blog du 18 février 2026 : aucune date ; notes de version sprint 268 : politiques obsolètes désactivées) : à revérifier avant citation.
  • Notion « Modern Lifecycle » (champ fresh et section pérennité) : non retrouvée dans une source Microsoft.
Written with AI assistance; sources are cited at the bottom of the page. An error? Report it via clement13.net.
Reading
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.exe confidently from the command line (get, add, checkout, checkin, undo, delete, history, merge, branch, status, workfold) and know the pitfalls (TF203069 and 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:

  1. Get: retrieve the latest version.
  2. Checkout (edit): indicate that you are modifying (explicit in server workspace, implicit in local workspace).
  3. Modify the files.
  4. 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
⚠️ Pitfall

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.

💡 Expert tip

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.

⚠️ Pitfall

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
✅ Good practice

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.

⚠️ Pitfall

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).

⚠️ Pitfall

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
💡 Expert tip

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.

⚠️ Pitfall

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.

🧪 Example

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:

  • .tfignore has 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 that tf delete also deletes locally by default.
  • If a change is already pending on the item, run tf undo first, otherwise TF203069 (see pitfalls).
  • tf delete refuses an item never fetched into the workspace (“not found in your workspace”): first do a tf get of the item.
  • The .tfignore must 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 (.gitignore and .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.

✅ Good practice

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 status in 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
⚠️ Pitfall

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#

🤖 AI

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#

✻ Written by Claude Sonnet 5.5