Git-TF is a set of command line tools that facilitate the use of a local Git repository with TFS. These tools make it easy to clone sources from TFS, fetch updates from TFS, and to update TFS with changes committed locally in Git.
The shelve command is still in development. The command should not be used with production data until it is updated in a future release.
Git-TF is supported on the following platforms:
Git-TF is supported for use with the following versions of TFS (servers):
Any of the following versions of the Java runtime (latest versions available from http://www.java.com):
It is recommended that the latest version of Git is installed as well. It is not strictly needed to use Git-TF, but it will be useful in working with the local repository created by the tools. Information about the latest Git downloads can be found on: http://git-scm.com/downloads
A typical workflow for an individual developer using the Git-TF tools are as follows.
For a team working with an existing Git repo, a developer sharing changes to TFS using Git-TF would use the following workflow.
Below is the list of commands that are currently supported by Git-TF. These commands can be run from a command window by typing "git tf <command name>".
Displays the list of available Git-TF commands.
Displays the detailed syntax for a given command, including all optional flags.
Initializes a new git repo from an existing path in a TFS server. When cloning from TFS, by default a shallow clone is performed, i.e. only the latest changeset is downloaded and used to create the first Git commit. The optional --deep flag may be used to clone each TFS changeset for the specified path into the new Git repo. Note, when using the --deep option, all future git tf fetch, git tf pull, and git tf checkin operations will default to --deep.
Configures an existing git repo to be able to communicate with a TFS server. Like the clone command, the --deep option may be used to set the default for future fetch and checkin operations.
Checks in the changes made in the Git repo into TFS. By default, the checkin command will create a single TFS changeset for the aggregate of all changes made on the current branch in Git since the last checkin to TFS. When used with the --deep option, a TFS changeset will be created for each Git commit on the current branch since the last checkin to TFS.
Fetches changes made in TFS as a new commit in Git, and references the new commit as FETCH_HEAD. By default, a single commit will be created in the Git repo with the aggregate changes since the last fetch. When used with the --deep option, a Git commit will be created for each TFS changeset that was created since the last fetch.
Fetches changes made in TFS as a new commit in Git, and merges the commit with the latest commit in the current branch. By default, the fetch performed by the pull command is shallow, but the --deep option may be used to create a Git commit for each TFS changeset created since the last fetch. Also, merge is used by default when pulling, but the --rebase option may be used to perform a rebase instead.
Shelve the changes made in the Git repo into TFS. Shelve operates similarly to the git tf checkin --shallow option in that all aggregate changes since the last checkin will be created as a single shelveset.
Unshelve the changes from the TFS shelveset into a stash in the git repository. To apply the shelveset content in the repository execute git stash apply. When the stash is applied in the repository, the changes downloaded by the unshelve command will be merged with the current HEAD.
Lists the shelvesets available on the server that match the name and owner specified. If the --details flag is specified, more shelveset details will be listed. This command can also be used to delete shelvesets from the server.
The Git-TF tool is most easily used by a single developer or multiple developers working independently with their own isolated Git repos. That is, each developer uses Git-TF to clone a local repo where they can then use Git to manage their local development that will eventually be checked in to TFS. In this "hub and spoke" configuration, all code is shared through TFS at the "hub" and each developer using Git becomes a "spoke". Developers looking to collaborate using Git's distributed sharing capabilities will want to work in a specific configuration described below.Most often, developers collaborating with Git have cloned from a common repo. When it comes time to share divergent changes, conflict resolution is easy because each repository shares the same common base version. Many times, conflicts are automatically resolved. One of the keys to this merging of histories is that each commit is assigned a unique identifier that is generated by the contents of the commit. When working with Git-TF, two repositories cloned from the same TFS path will not have the same commit IDs unless the clones were done at the same point in TFS history, and with the same depth. In the event that two Git repos that were independently cloned using Git-TF share changes directly, the result will be a baseless merge of the repositories and a large number of conflicts. For this reason, it is not recommended that teams using Git-TF ever share changes directly through Git (i.e. using git push and git pull).
Instead, it is recommended that a team working with Git-TF and collaborating with Git do so by designating a single repo as the point of contact with TFS. This configuration may look as follows for a team of three developers:
[TFS] [Shared Git repo] | ^ (2) | \ | / | \ | / | \ V (1) / V (3) V (4) [Alice's Repo] [Bob's Repo] [Charlie's Repo]
In the configuration above the actions would be as follows:
Both Bob and Charlie only ever interact with the team's shared Git repo using git push and git pull. They can also interact directly with one another's repos (or with Alice's) , but should never use Git-TF commands to interact with TFS.
When working with the team, Alice will typically develop locally and use git push and git pull to share changes with the team. When the team decides they have changes to share with TFS, Alice will use a git tf checkin to share those changes (typically a git tf checkin --shallow will be used). Likewise, if there are changes that the team needs from TFS, Alice will perform a git tf pull, using the --merge or --rebase options as appropriate, and then use git push to share the changes with the team.
Once changes have been fetched from TFS using git tf pull (or git tf fetch), those changes must either be merged with the HEAD or have any changes since the last fetch rebased on top of FETCH_HEAD. Git-TF allows developers to work in either manner, though if the repo that is sharing changes with TFS has shared any commits with other Git users, then this rebase may result in significant conflicts (see The Perils of Rebasing). For this reason, it is recommended that any team working in the aforementioned team configuration use git tf pull with the default --merge option (or use git merge FETCH_HEAD to incorporate changes made in TFS after fetching manually).
When using the Git-TF tools, there are a few recommended settings that should make it easier to work with other developers that are using TFS.
Git has a feature to allow line endings to be normalized for a repository, and it provides options for how those line endings should be set when files are checked out. TFS does not have any feature to normalize line endings - it stores exactly what is checked in by the user. When using Git-TF, choosing to normalize line endings to Unix-style line endings (LF) will likely result in TFS users (especially those using VS) changing the line endings back to Windows-style line endings (CRLF). As a result, it is recommended to set the core.autocrlf option to false, which will keep line endings unchanged in the Git repo.
TFS does not allow multiple files that differ only in case to exist in the same folder at the same time. Git users working on non-Windows machines could commit files to their repo that differ only in case, and attempting to check in those changes to TFS will result in an error. To avoid these types of errors, the core.ignorecase option should be set to true.