Cleaning up Main to Dev changesets - visual-studio

My Main branch seems to have change sets that my Dev branch does not have, but they are older. How should/can I clean up the changes sets?
The following is trying to merge Main into Dev and says there are changes. My understanding is that if Main and Dev match I should not see change sets during this merge anymore.
If I merge from Dev to Main I see no change sets.
Should I merge these even though newer change sets have already been applied? Example changes were made and merge into Main on 4/25.
Will this overwrite current code or will it really just update histories?

You could use tf merge command with /discard option.
/discard Does not perform the merge operation, but updates the merge
history to track that the merge occurred. This discards a changeset
from being used for a particular merge.
Sample command:
tf merge $/Project/SourceBranch $/Project/TargetBranch /discard /recursive /version:C56693~C56693
It discards changeset 56693. The version is a from ~ to, so you can discard multiple changesets at once. When the command has finished, you still need to check in the merge.

Related

What is the optimal way to only sync certain file extensions and exclude other file extensions between separate git branches?

Given 3 branches say master, b1, and b2.
The master branch only cares about *.txt files. It needs to ignore everything else.
Branch b1 only needs what is included by master and say *.h, *.c, *.cpp files, and ignore everything else.
Branch b2 also only needs to include the ones included by master and say *.jpg, *.png, *.html, *.css etc. ignoring everything else.
In short, master branch contains information only common to all branches. Example use case: Branch b1 is used to generate output files to be consumed by branch b2, but both contain some information shared with master.
So, what is the optimal way to sync only those common files between master, b1, and b2, and have each branch to only include certain file extensions in its branch and ignore everything else it doesn't need?
I also looked at alternatives of having separate git repos or submodules or subtrees, but the directory structures or nesting patterns created little difficulties. Is there a better way to solve this problem?
Let me start with this because it's perhaps more useful:
Is there a better way to solve this problem?
You could, in theory, not bother with a master branch at all. Have three branches, none of which holds final-assembled-results. Do the assembly outside of Git. If desired, make an "orphan branch" (or use a tag) to record the assembled result, or just keep the assembled results in a completely different repository. But these all result in those sorts of little difficulties you mention.
What goes wrong
Git simply doesn't work the way you want: you cannot (usefully anyway) "care about" some files and have other "uncared-about" files with the "caring" switching around based on the branch. That's because "branches", in the sense you're using the word, do not exist.
Now, that's a strong statement and it needs justification. Clearly, branches do exist. The problem lies in the meaning of the word branch. It has too many meanings and people just sort of flip between them, without realizing that they're doing this, and that gets them into trouble. (See also What exactly do we mean by "branch"?) So let's just avoid the term by using what Git really uses: commit hash IDs.
When you run:
git checkout br2
you're telling Git to do two things:
save the name br2 for future use;
turn the name br2 into a commit hash ID, and extract—which includes "caring about"—a snapshot of all files from that commit.
The second step is the one that really matters right now: the first one is only needed later, when you run git commit to make a new commit, or some other Git command that needs the name (git branch or git status or git rebase, for instance).
With one exception—which you see in a fresh clone that hasn't yet run git checkout—Git always has some commit checked out right now. Your git checkout tells Git: sweep away the one we have right now, and get me some other commit as the checked-out commit.
Let's say that right now, you have br1 checked out, which is commit b100 right now. Later, the name br1 may mean some other commit, but right now it means that one. You run git checkout br2, which tells Git to switch from commit b100 to commit b200 as that's the one that the name br2 means right now.1
OK, no big deal yet, right? We're moving from commit b100 to commit b200. Commit b100 has in it the *.h files and omits the *.jpg files entirely. So Git "cares about" the *.h files while we have b100 out. Those files are tracked, which means they're in the (single) index. We're moving off b100 though, to b200, which has the *.jpg files and omits the *.h files. Git has to copy the *.jpg files into its index and remove the *.h files from its index, which means it has to remove the *.h files from your work-tree too.
So far, this is all going great: you get just what you want. But now you want to get to master and assemble the pieces. The name master means some other commit, maybe a123 at the moment.
No matter how you get to master, from br1 (b100 at the moment) or br2 (b200) at the moment, you don't have all the *.h and *.jpg files. You can only get one set or another. The underlying problem here is that the "caring about" happens because the files are in Git's index. Listing files in a .gitignore file, which is what you do to keep them from getting into Git's index, only helps if they're not already there—and when you switch to a commit that has the files, Git will put them into Git's index, regardless of what's in a .gitignore file. When you switch to a commit that omits the files, Git will remove them from Git's index, regardless of what's in a .gitignore file.
The index's contents reflect the commit you check out. Each commit has a full snapshot of every file that's in that commit. That snapshot winds up in Git's index. Unless you change them—with git add, or git rm, or by doing another git checkout that replaces them wholesale, for instance—those are the files that will go into the next commit.
Last, when you use git merge to combine work, Git:
finds a merge base commit;
compares the two branch tip commits against this merge base; and
uses that to figure out what to put into the new commit.
The new commit, like any commit, has a snapshot of all the files: all the files that were in Git's index at the time git merge made the merge commit, and those files are the result of the combining process above. Merge commits are the same as any other commit: they have a snapshot and metadata. The only thing that makes them special—makes them merge commits—is that they have two (or more) parent commit hash IDs listed in their metadata.
These interlocking behaviors get in the way: Either master actually does have all the files, in which case, the other commits found by other branch names also need to have all the files, or master doesn't have any of the files, in which case the other branches can be exclusive like this but you can't merge them back into master, because the common commit that Git will find, that will act as the merge base, will cause them to add the files to the new commit that goes into master—and now master has all the files! If you remove them as you go back into the branches, merging will remove the files this time.
Ultimately, Git is all about commits. It's the commits that determine, well, everything! The commits are snapshots-plus-metadata. All a branch name does is find one particular commit: the last one on some chain. Commits can be reached from more than one branch name, and many, or most, commits are on multiple branches simultaneously. So the name has nothing to do with which files are in the commit: it literally can't when more than one name finds that commit.
1Branch name to commit hash ID mappings change, which is how branches grow in Git. Git is built to add new commits, so the normal way that a name changes is that it now means a newer commit that leads, via the commit graph, back to the old commit—and many more commits too. See also Think Like (a) Git.

TFS Merge Specific Change Set - Change Set Missing

A google search and a specific stockoverflow search hasn't found me anyone else with this problem.
In tfs our procedures are that we make changes in a branch called Dev, than at a later time merge specific changesets up to a branch called DevQA. (We have other branches as well which shouldn't be affecting things at this point.) We use screens available in VS to do this.
I have 9 changesets which I made last week (all relating to the same or at least overlapping code) and it's now time to merge some of those up to DevQA.
The changes are showing in History on Dev, but when I choose merge, then specific change set (set the source to Dev and the target to DevQA) and click next the list of change sets available to merge does not include 7 of my 9 changes sets, including the ones I want to merge.
I've done some checks and they appear to be the only missing changesets, others have either been merged already, or are available in the list.
I have clicked on one of the changesets and used the 'track changeset' & Visualise options, and it shows the change set has NOT already been merged.
A new branch was created after these changesets were made, which was branched from DEV and my changesets show as being in that. However I don't believe this is related because other changesets which are in that ARE available to merge to DevQA.
Is there any reason these may not be showing up? Is there any way I can work around this and force them to merge?
If the changesets are in sequence or you don't mind to merge other changesets which in the range of your nine changesets, as a workaround you could try to use tf command.
Using the tf command line tool you specify a range of versions by separating the version with a tilde character.
tf merge /recursive /version:C1000~C1008 "$/SourceBranch" "$/TargetBranch"
In this case the changes 1000 and 1008 will also be included.
To merge multiple separate changesets into another branch you will have to do it in multiple steps:
tf merge /recursive /version:C1001~C1001 "$/SourceBranch" "$/TargetBranch"
Then your workspace for the target branch will contain the changes for both changesets and now you can check-in the merges as one changeset in the target branch.

Revoking TFS Merge Credits

I understand that when performing a merge from BRANCHA > BRANCHB and selecting “Keep Target” on some files will result in “Merge Credits” being granted to those files in TFS history: Merging Developement Branch to Main: There were no changes to merge
Is there a way for us to revoke/wipe the Merge Credits on given files, effectively enabling our developers to perform a /force merge via Source Control Explorer?
Context:
On a weekly basis, we merge from BRANCHA > BRANCHB to synchronize parallel efforts. Any files with unresolved conflicts are noted and their owners are notified to perform a merge on those files from A > B. We then select “Keep Target” on these files – anticipating a future merge by the file owner to resolve the pending conflict. We’re delegating the work out to the individual owners.
This doesn’t quite work though, because TFS will see that the initial merge has “resolved” the conflict (basing its evaluation on prior merge history, not file contents).
I want to enable developers to perform re-merges on these files without forcing them to use the command prompt and /force switch.
Moving forward, my plan is to “Undo Changes” on those pending changes that were selected as “Keep Target” before checking in a branch>branch merge. This will enable the devs to perform the merges independently by dodging the merge credits.
But looking back, a branch<>branch /force merge results in hundreds of conflicts that we would like to delegate out to the owners as a means of syncing up two branches. Performing a classic merge reports nothing on these because prior merges have “Kept Target”.
You're indeed better off undoing the merge for the specific files you don't want to merge right now, but once you've chosen keep target, TFS indeed remembers that choice.
If you're already past the point of committing the merge, there is a way to get rid of the ticket, and that is to roll back the merge. You can roll back the merge for a group of individual files.
The trick here is as follows:
Look up each file and them in the Source Control Explorer.
Right-click the individual file and from the context menu choose to Rollback....
Enter the changeset number of the merge
Repeat 1 to 3 until you have all the file in your list.
Check-in this compensating changeset.
Try merging Branch A to Branch B again, your candidate changes should show up again.
You can do the same thing from the commandline, using tf rollback (pre-2015) or tf vc rollback (post-20215)

tfs re-merge doesn't work

So rolled back my merge successfully in vs 2008 using the tfs powertools tfpt command.
I tried to re-merge and it said there were no changes (but there are).
Which brings another thought to mind. When I checked in my merge it had about 20 files in the check in even though I only changed 2 files. Now, when I did the rollback it only gave me back the 2 files that I changed to check in?
Thats the normal TFS behavior. This is how TFS rollback works, TFS rollback is not actually a "rollback" per say, it's a new changeset overwriting the latest version with an old version!
You have branches A (parent) and B (child). You made some changes to B and then did a merge to A. TFS now assumes that all changes from B is present in A and updates it's internal DB with timestamps. When you rolled back the changes in A (parent) it doesnt change the updates made by TFS to its internal DB. TFS just considers that as a new change! So for TFS there are no changes between B and A, because nothing has changed in B since the last merge! Makes sense?
"When I checked in my merge it had about 20 files in the check in even though I only changed 2 files. Now, when I did the rollback it only gave me back the 2 files that I changed to check in?"
When you say merge shows 20 files -> if you open the merge changeset you can actually see which all files got edit and which all just got merged. Against each file in the change set there will be an entry saying "merge, edit"/ "merge"/"branch"/"delete" etc. Those tell you what exactly happened with that merge for the particular file.
In your case I am assuming you have "merge" next to those 18 files. Rename is the most common reason that I have seen for this. There are a number of possible reasons. This is not a complete list:
You performed a namespace operation (delete, undelete, rename) on a parent folder of the files marked "merge"
You performed a namespace operation (delete, undelete, rename) that had already been performed in the target branch
You performed a sequence of namespace operations that collapsed into a no-op (eg delete + undelete, or rename a -> b -> a)
There are unresolved conflicts
You performing a discard

Tfs2010: How do I get the server path of the source file in a rename operation using a pending change in a shelf?

When I perform tf rename $/Project/Main/File1.cs $/Project/Main/File2.cs in TFS2010, I know that once I check in there will be a "rename" change on the $/Project/Main/File2.cs slot, and a "delete, source rename" change on the $/Project/Main/File1.cs slot.
However, while the changes are still pending, only the rename change exists as a pending change. No changes are displayed in Pending Change to indicate that $/Project/Main/File1.cs is being renamed. In fact, if you execute tf status $/Project/Main/File1.cs tf.exe claims there are no pending changes, which is totally false.
In my situation, I have a series of about 100 files that I have manually merged as part of a branch integration operation, and following a re-execution of the tf merge command at the command line, I am simply trying to undo the files to which they apply so that I may unshelve the merged changes.
However, the Tfs object model's PendingChange objects can supply me only with the ServerPath, which refers to the "source rename" item, not the "rename" item. I am at a loss about how I can trace my shelved pending changes to the items that would need to be undone in my workspace.
How can I get the original pre-rename server path for items in a shelf that have been renamed?
You could get specific version to a point in time that the files mapped correctly and take note of the paths there.
Also, you could try TF Rollback. If you supply the changeset where you manually merged those 100 files you will, at the very least, get a list of all relevant files if you rollback (you dont have to checkin the rollback but rather just use it to visualize the files). From there, you should be able to figure out the related files.

Resources