Using Team Foundation Server For Version Control From A Linux Client

Access TFS From Linux!

The Microsoft Team Foundation Cross-Platform Command-Line Client allows developers to access Team Foundation Server for version control.

Preparation

Before installation, take some time to consider how you will setup your workspace path. My previous post, Mounting A Directory In Linux will help provide a couple of things to consider, such as mitigating paths that may become too long for TFS checkins.

Download The Client

Download the Anywhere Client and unzip.

Setup The Environment Variables

For

sh

edit

~/.profile

For

bash

edit

~/.bashrc

Add an alias so you don’t have to type the full path to the executable

alias tf='/home/user/path/to/anywhere-client/TEE-CLS-x.x.x/tf'

You can also consider setting the following environment variables to make things a bit smoother.

Save yourself from having to re-type your credentials each time you execute a command

export TF_AUTO_SAVE_CREDENTIALS='1'

Setup your diff and merge tool of choice. In this example I’m using Beyond Compare.

export TF_DIFF_COMMAND='/usr/bin/bcompare %1 %2'
export TF_MERGE_COMMAND='/usr/bin/bcompare %1 %2 %4'

Setup The Workspace

Before setting up a workspace, if you’re unsure if one already exists for the machine, you can check with

tf workspaces

This also prints the Collection after you’ve setup a workspace if you want a quick reminder of which one is being used.

To create a new workspace, have the Collection url and your credentials available and use

tf workspace -new my-workspace-name -collection:http://tfsserver:8080/tfs/Collection

Map A Project

Project directories are mapped with the workfold command

tf workfold -map -collection:'http://collection:8080/tfs/collectionname' -workspace:workspace-name $/TFS/Project/Path /local/project/path/target

Pulling Code

To pull a project

tf get .

or

tf get -recursive some/nested/folder/path/

Ignore File

Much like the .gitignore and .hgignore files, you can create the .tfignore for TFS to ignore directories and files. For example

touch .tfignore
vim .tfignore

Sample file contents

*\bin\*
*\obj\*
*\dist\*
*.txt
*\packages\*

Checkout

Checking out a file can be as granular as a specific file or as broad in scope as multiple directories. Note that simply editing a code file will mark it as changed. To checkout the current location and subdirectories

tf checkout -recursive .

Getting The Status of TFS Changes

To view the status of detected TFS changes between the TFS server and the local machine

tf status .

diffing files

Using the appplication set in the Environment Variable

TF_DIFF_COMMAND

mentioned above, you can view the diff between the server version and your local version with

tf difference filename

Checkin

Here’s an example of creating, adding and checking in a file for version control.

touch newfilename
tf add newfilename
tf checkin -comment:'Adding a new file named newfilename'

More Help

There are many more options beyond these, “Getting Started” basics. Here is the output of

tf -h

at the time of this writing for reference.

Team Explorer Everywhere Command Line Client (version 14.114.0.201703081734)

 Available commands and their options:

  add         [-lock:none|checkin|checkout] [-type:<value>] [-recursive] [-silent] [-noignore]
              <localItemSpec>...
  branch      [-version:<value>] [-noget] [-lock:none|checkin|checkout] [-recursive] [-checkin]
              [-comment:<value>|@valuefile] [-author:<value>]
              [-notes:'note'='value'[;'note2'='value2'[;...]]|@notefile] <oldItemSpec> <newLocalItem>
  branches    [-version:<value>] <itemSpec>...
  changeset   [-latest] [-comment:<value>|@valuefile]
              [-notes:'note'='value'[;'note2'='value2'[;...]]|@notefile] [changenumber]
  checkin     [-all] [-author:<value>] [-comment:<value>|@valuefile]
              [-notes:'note'='value'[;'note2'='value2'[;...]]|@notefile] [-override:<value>|@valuefile]
              [-recursive] [-validate] [-bypass] [-force] [-noautoresolve]
              [-associate:<workItemID>[,<workItemID>...]] [-resolve:<workItemID>[,<workItemID>...]]
              [-saved] [<itemSpec>...]
  checkout    [-recursive] [-lock:none|checkin|checkout] [-type:<value>] <itemSpec>...
  delete      [-lock:none|checkin|checkout] [-recursive] <itemSpec>...
  delete      -detect [-lock:none|checkin|checkout] [-recursive]
  destroy     [-keephistory] [-startcleanup] [-preview] [-silent] [-stopat:<value>] [-noprompt]
              itemspec1[;versionspec][;XdeletionID] [itemspec2...itemspecN]
  difference  [-recursive] <itemSpec> <itemSpec2>
  difference  [-shelveset:<value>] [-recursive] <shelvesetItemSpec>
  difference  [-version:<value>] [-recursive] <itemSpec>
  dir         [-version:<value>] [-recursive] [-folders] [-deleted] <itemSpec>...
  eula        [-accept]
  get         [-version:<value>] [-recursive] [-preview] [-force] [-all] [-overwrite] [-noautoresolve]
              [<itemSpec>...]
  getcs       -changeset:changeset [-latest]
  help        [-listexitcodes] [<command>]
  history     [-version:<value>] [-stopafter:<value>] [-recursive] [-user:<value>]
              [-format:brief|detailed|xml] [-slotmode] [-itemmode] <itemSpec>
  info        [-recursive] [-version:<value>] <itemSpec>...
  label       [-owner:<value>] [-version:<value>] [-comment:<value>|@valuefile]
              [-child:fail|replace|merge] [-recursive] <labelName>[@<scope>] <itemSpec>...
  label       -delete [-owner:<value>] [-version:<value>] <labelName>[@<scope>]
  labels      [-owner:<value>] [-format:brief|detailed|xml] [<labelNameFilter>]
  lock        [-recursive] [-lock:none|checkin|checkout] <itemSpec>...
  merge       [-recursive] [-force] [-candidate] [-discard] [-version:<value>]
              [-lock:none|checkin|checkout] [-preview] [-baseless] [-nosummary] [-noimplicitbaseless]
              [-format:brief|detailed|xml] [-noautoresolve] <source> <destination>
  merges      [-recursive] [-format:brief|detailed|xml] [<sourceItem>] <destinationItem>
  online      [-adds] [-deletes] [-diff] [-exclude:<value>[,<value>]] [-recursive] [-preview]
              [<itemSpec>...]
  print       [-version:<value>] <itemSpec>
  property    [-output:<value>] [-recursive] [-version:<value>] <itemSpec> [<propertyname>]
  property    -deleteall [-recursive] <itemSpec>
  property    -deletevalues:<value>[,<value>] [-recursive] <itemSpec>
  property    -setvalues:@valuefile|name1=value1[;name2=value2;name3=@valuefile;...] [-recursive]
              <itemSpec>
  reconcile   [-teamProject:<value>]
  reconcile   -buildName:<value> [-teamProject:<value>] [-recursive] [<itemSpec>...]
  reconcile   -changeset:changeset [-recursive] [<itemSpec>...]
  reconcile   -forgetBuild:<buildName> [-teamProject:<value>]
  rename      [-lock:none|checkin|checkout] <oldItem> <newItem>
  resolve     [-auto:AutoMerge|TakeTheirs|KeepYours|OverwriteLocal|DeleteConflict|KeepYoursRenameTheirs|
              External] [-preview] [-converttotype:<value>] [-recursive] [-newname:<value>] <itemSpec>
  resolve     [-auto:AutoMerge|TakeTheirs|KeepYours|OverwriteLocal|DeleteConflict|KeepYoursRenameTheirs|
              External] [-preview] [-overridetype:<value>] [-recursive] [-newname:<value>] <itemSpec>
  resolvePath [-collection:<url>] [-workspace:<value>] <serverPath>
  resolvePath <serverPath>
  rollback    -changeset:changesetfrom~changesetto [-recursive] [-lock:none|checkin|checkout]
              [-version:<value>] [-keepmergehistory] [-noautoresolve] [<itemSpec>...]
  rollback    -toversion:versionspec [-recursive] [-lock:none|checkin|checkout] [-version:<value>]
              [-keepmergehistory] [-noautoresolve] <itemSpec>...
  shelve      [-move] [-replace] [-comment:<value>|@valuefile] [-recursive] [-validate] [-saved]
              <shelvesetName[;owner]> <fileSpec>...
  shelve      [-replace] [-comment:<value>|@valuefile] [-validate] [-saved] <shelvesetName[;owner]>
  shelve      -delete [-collection:<url>] [-validate] [-saved] <shelvesetName[;owner]>
  shelvesets  [-owner:<value>] [-format:brief|detailed|xml] [<shelvesetName>]
  status      [-workspace:<value>] [-shelveset:<value>] [-format:brief|detailed|xml] [-recursive]
              [-user:<value>] [-nodetect] [<itemSpec>...]
  undelete    [-noget] [-lock:none|checkin|checkout] [-newname:<value>] <itemSpec>[;deletionID]...
  undelete    [-noget] [-lock:none|checkin|checkout] [-recursive] <itemSpec>[;deletionID]...
  undo        [-recursive] <itemSpec>...
  unlabel     [-recursive] <labelName>[@<scope>] <itemSpec>...
  unshelve    [-move] [-recursive] [-nomerge] [-noautoresolve] [-format:brief|detailed|xml]
              <shelvesetName[;owner]> [<itemSpec>...]
  uu          [-recursive] [-noprompt] <itemSpec>...
  workfold    [-collection:<url>] [-workspace:<value>] <serverFolder>
  workfold    [-map] [-collection:<url>] [-workspace:<value>] <serverFolder> <localFolder>
  workfold    [-workspace:<value>]
  workfold    <localFolder>
  workfold    -cloak [-collection:<url>] [-workspace:<value>] <serverFolder>|<localFolder>
  workfold    -decloak [-collection:<url>] [-workspace:<value>] <serverFolder>|<localFolder>
  workfold    -unmap [-collection:<url>] [-workspace:<value>] <serverFolder>|<localFolder>
  workspace   [-collection:<url>] [-comment:<value>|@valuefile] [-newname:<value>]
              [-filetime:current|checkin] [-permission:Private|PublicLimited|Public]
              [<workspacename;[workspaceowner]>]
  workspace   -delete [-collection:<url>] [<workspacename;[workspaceowner]>]
  workspace   -new [-noprompt] [-template:<value>] [-computer:<value>] [-comment:<value>|@valuefile]
              [-collection:<url>] [-location:server|local] [-filetime:current|checkin]
              [-permission:Private|PublicLimited|Public] [<workspacename;[workspaceowner]>]
  workspaces  [-owner:<value>] [-computer:<value>] [-collection:<url>] [-format:brief|detailed|xml]
              [-updateUserName:<user@domain>|<domain\user>] [-updateComputerName:<value>] workspaceName
  workspaces  -remove:<workspace1>[,<workspace2>,...] -collection:<url>

 Options accepted by most commands:

  [-login:domain\username,password] | [-login:username@domain,password] 
  -collection:<url>                                                     
  -continueOnError                                                      
  -exitcode                                                             
  -help                                                                 
  -noprompt                                                             
  -nosummary                                                            
  -outputSeparator:<value>                                              
  -proxy:<url>                                                          
  -server:<url>                                                         
  -workspace:<value>                                                    

Options may be started with any of: - 

For general help, view the 'help/index.htm' file installed with the program.
For help on a specific command, supply the command name as an argument
to the help command.  For example: 'tf help checkin'

Leave a Reply