1

We will be migrating our TFS/TFVC repository to Git. In TFVC we used to have a trunk based development with long lasting release maintenance branches. Bug fixes on release branches have to be merged back to the trunk. Sometimes smaller features have to be carried over from trunk to a release branch. In TFVC we did this by "merging" individual (or small groups of) changesets from one branch to the other. The resulting changesets were marked as "merge", although I don't exactly know what that implies for TFVC, especially considering future merge operations.

So I imagine the branch graph to look something like this: (Although note that TFVC never displays any graphs)

-A--B--C---D--E--F---    trunk
  \       /    \
   G--H--I--J---K--L-    release-x

(Here the release branch has been created from A, I->D is a bugfix merge, E->K is a feature-forward) But maybe I'm wrong. In this case could someone explain what a TFVC merge changeset really does?

I've been told that an equivalent way of doing in Git is cherry-picking individual commits. However, on the resulting branch graphs, I don't see any link between the branches following a cherry-pick commit. I am aware that cherry-picking is not technically a merge operation and thus history relation between the branches is not carried over. Is there something I am missing? Is there a better way of carrying over such small commits from one branch to another, yet still retain some merge information? I DO NOT want to merge the entire branch. As for example changesets B,C or H must remain isolated from each other branch.

Scrontch
  • 3,275
  • 5
  • 30
  • 45
  • Cherry-picking is the way to go. In a future merge git will see the changes have already been merged. The alternative would be a rebase mess. – jessehouwing Dec 01 '21 at 16:06
  • Can you clarify the last two sentences? If you did merges as suggested in your graph, B,C, and H are already all in both `trunk` and `release-x`. The *only* differences between the two branches should be F is only on `trunk` and J and L are only on `release-x`. – TTT Dec 01 '21 at 20:31
  • @TTT: I did the merges as "merge of selected changesets" (in TFVC terminology), not as merges of a branch. Maybe my understanding of this is false though and this doesn't mean much. – Scrontch Dec 01 '21 at 20:51
  • OK- sorry about that. You did say that in the question but I missed it and just went by the graph. The phrasing "merging individual changesets" is strange from a Git point of view. Perhaps a better graph would be to remove the lines from I-D and E-K, and change D to I', and K to E'. That would represent a cherry-pick of those changesets and would be clearer (at least to me). – TTT Dec 01 '21 at 22:05
  • Yeah, the thing is, TFVC does label them (D or K) as merge changesets before the check-in, so it somehow stores information that they partly came from another branch. Git-cherrypick doesn't do so afaik. – Scrontch Dec 01 '21 at 22:12

1 Answers1

2

When Cherry picking you can add the -x option to have git add a message to the commit-message of each cherry-picked commit:

-x When recording the commit, append a line that says "(cherry picked from commit …​)" to the original commit message in order to indicate which commit this change was cherry-picked from. This is done only for cherry picks without conflicts. Do not use this option if you are cherry-picking from your private branch because the information is useless to the recipient. If on the other hand you are cherry-picking between two publicly visible branches (e.g. backporting a fix to a maintenance branch for an older release from a development branch), adding this information can be useful.

This is the closest you'll get. A cherry picked commit it recorded as a new commit on the target branch, not as a reverse or forward integration.

Git doesn't do "partial merges" since git stores the status of the worktree as a "version". TFVC stores the workspace versions of individual files instead.

What you're seeing are 2 very different things.

In TFVC you see a branch graph that is clearly defined through branch objects that have a defined parent branch. Each integration between those branches is visualized, since the merge stores which files were merged, which changesets were part of that merge and over which branch relationship these were merged. A changeset in TFVC (closest thing to a commit in git) is also a very different thing. For one, a changeset can span multiple branches, even repositories, and a changeset contains 1 or more changes to files.

In Git a branch is just a special pointer to a commit somewhere in the commit graph. Parentage is calculated (based on the commit graph) and branches can freely merge without respecting a preconfigured hierarchy. The visualization shows the relationships between commits, not branches. A special type of commit, the merge, stores when a new commit was created from the basis of 2 or more other commits. The relation to these commits is stored, not the names of the branches that happened to point to these commits. A Commit stores the changes to the worktree. One or more branches may point to that commit.

Since a cherry-pick replays the changes on one branch in the commit tree onto another, it's not recorded as a merge where multiple sets of commits are linked together. Instead, it's recorded as a new commit that happens to have the same diff (and potentially not even that if you needed to do conflict resolution).

By adding the -x flag the relationship is stored in the comments.

Scrontch
  • 3,275
  • 5
  • 30
  • 45
jessehouwing
  • 106,458
  • 22
  • 256
  • 341
  • Thank you for this answer! I've still a question though: I've converted a TFVC repo to Git using git-tfs. The initial TFVC repo contained such single changeset merges between branches. When viewing the resulting Git repo in SourceTree, i can see the graphs of branches merged at those changesets (which is nice). So how did git-tfs do preserve the source branch information? – Scrontch Dec 06 '21 at 17:21
  • 1
    It creates a merge, doesn't immediately commit it and then only retains the changes of the files that were merged. This can lead to strange behavior when you merge at a later time, as git will think the other changes were discarded. – jessehouwing Dec 06 '21 at 19:19
  • Could I do this by hand as well? What would be the sequence of git commands? The feature branches will never be merged back into the main branch as a whole. – Scrontch Dec 13 '21 at 16:09
  • Something like git merge, undo all files you don't want to merge, commit. – jessehouwing Dec 13 '21 at 17:55