git rebase interactive already pushed commits - git-rebase

Recently I had a lot of success rebasing a local-only repository while changing commit messages and after that only the commit messages had been changed but the history itself hasn't.
Now I have my repositories - remotely and locally. I made several commits on several branches and already pushed them. Due some reasons I need to change some commit massages over several branches and tried to use rebase interactive like before. But the commits appeared at the end of the current checked out branch.
(I know how to and I had reset my repositories to the state before rebasing.)
After some reading I realized the problem is the commits had been pushed already which wasn't the fact in my local-only repository.
I tried to rebase the remote repository, but it's a bare one - so it didn't work.
I know its not recommended. But for learning purposes I really like to know how to change several commit messages without resulting in duplicate commits at the end of my branch / repository.
(I don't like this solution: copy and to change my local repository to a bare one as my new remote repository, which would solve my problem.)
I hope I made myself clear enough. Thanks.

Changing the commit message results in changing the commit's hash value and this means all following commits have to change their hashes too (as the parent is included in the hash computation, as the message itself is too).
This is why rebasing is typically only allowed in local branches. Many or say most git remote repositories permit rewriting the pushed history as anyone could have downloaded it already and would then work on a outdated history/branch/commit.
Strategy for preventing or catching git history rewrite
However if your server does allow history rewrites (e.g. if you are the only one working on it) you may push them with --force.
As a side note see here https://stackoverflow.com/a/5668050/1756183
Edit rebase with several branches:
C1 <- C2 <- C3 (branch1)
rebasing children of C1 results in
C1 <- CR2 <- CR2 (branch1)
But if you have:
/ C4 <- C5 (branch2)
C1 <- C2 <- C3 (branch1)
rebasing will most likely lead to:
/ C2 <- C4 <- C5 (branch2)
C1 <- CR2 <- CR3 (branch1)
the reason is that C2 is still the parent of C4, the "fixed" commit CR2 is only related to the rewritten Branch branch1.
If you want to "forget" C2 you have to rebase C4 on top of CR2 (you ll have to play around with rebase --onto). After that C2 is not adressed as anyone's parent or on any branch and will not be shown in the history (although it is still there until garbage collected).

Related

commit history after merging with tvfc

I have a branch B which is originated from branch A. I make 3 commits named c1, c2, c3 to branch A and then merge with branch B (using the wizard). But in source control explorer I don't seem to be able to see the 3 commits that I've made in B.
The history for branch A is:
c0 c4(merge with branch B)
^
|
|
here I expected to see c1, c2, c3
In git, we could see those using git log . How can I see them in tfvc? Are they there and just hidden or just doesn't care about those changesets.
Bottom line is I've seen pictures when there are tiny triangles on left side of each changeset in source control manager which shows the child branch commits on click. How can I have the same thing with TFVC?
thanks for your time.
-- I expected to be able to see all of the commits in branch B to be visible after merging in branch A.

How to push only a subset of committed files?

We recently switched from svn to mercurial. We're using Aptana as our IDE with the MercurialEclipse plugin, along with BitBucket for our repositories and SourceTree as our (additional) source control GUI.
I created 2 new files in Aptana, and committed each of them. Now in the Synchronize view, where the 2 files are listed as "outgoing", I'd like to push only one of them. I avoided using the "push all" icon at the top which would push all outgoing changes - instead I right-clicked a specific file in the outgoing list and chose "push" from the context menu. However, this caused both outgoing changes to be pushed. I can't seem to find any option to push only a specific file or subset of files of the committed changes. Is there any way to accomplish this in Aptana?
Note: My answer doesn't relate to Aptana, but instead covers what I think your issue is.
I think the main problem is a misunderstanding of how Mercurial stores its changes, which coming from a Subversion background is perfectly reasonable.
In Subversion, change history can be considered to be stored per file. That is, if you change two files and commit them, you can easily, and often do, have a situation where files in your working copy are at different versions.
In Mercurial, change history is stored across the whole repository. Committing will create a new "Changeset", which stores the state of the entire repository at that time. When you decide to push a change out to another repository, all modifications (or adds, or deletes, or...) will be pushed out with that change.
A caveat is that when you decide to commit a new changeset to your repository, you can selectively include or exclude files. Files not included will remain in your working copy as pending modifications, which can be committed in a new changeset.
I hope that makes sense to you - if you already understand it, it's a logical concept, but I find it tricky to explain.
So, on to your problem.
Lets say you have two files in your repository, file1 and file2 (it's that or foo and bar). You've changed them both, but they relate to different issues - they can be committed as different changesets:
$ hg log
changeset 0:....
summary: First commit
$ hg st
M file1
M file2
$ hg commit -I file1 -m "Changed file1"
$ hg log
changeset 1:....
summary: Changed file1
changeset 0:....
summary: First commit
$ hg st
M file2
Here you can see that we've committed only one file into the repository, and it's made a new changeset with the complete state of the repository at that time, minus the changes to file2. We can now do the same, committing file2, which will create another changeset. The problem with this approach is that changesets are ordered according to their parent, and so you couldn't easily push just the change to file2, without also pushing its parent - but it may be closer to what you're after.
TL;DR : SVN stores the state of individual files, Mercurial stores the state of the repository as a whole.
I very much recommend reading Mercurial: The Definitive Guide. It's a little out-of-date in places, but I think it will do a much better job of getting the concept across.

Propagation of changes in Mercurial

I have a following question. Suppose I have a bunch of repositories hosted that form an hierarchy, e.g.: A -> B -> C (means A is the central repository and all the rest are it's descendants).
Now suppose I work with the clone of C. Suppose I want to get the changes not just from C, but from the central repository, so I do the following commands:
hg pull [Address of A]
hg up
That seems perfectly legal, but what happens then I commit my changes and push them to C? Not only my local modifications will be pushed, but also the modifications of central repository (if there are any). What will happen if someone will try to pull the changes from A to C? Will there be a conflict or it will merge successfully the changes A -> local -> C with changes A -> C. Will Mercurial recognize it as the same changeset or not?
The identical situation takes place if I decide that my code is stable enough and can be placed in central repository:
hg commit -u spirit -m "A local modification that is stable"
hg push [Address of A]
What will happen if I make a pull from A to C and then pull from C to my local repo again, will it recognize these changeset as originating from my local repo, or will it report a conflict and suggest a merge?
And what is the best practice in that case anyway? Performing just subsequent pulls and pushes (i.e. A<->B, B<->C, C<->local)? But the problem is that I have just access to my local repository that is clone of C. How can I make a pull from B to C if I would want to on my local machine? How does Mercurial handle
What will happen if someone will try to pull the changes from A to C? Will there be a conflict or it will merge successfully the changes A -> local -> C with changes A -> C. Will Mercurial recognize it as the same changeset or not?
The changesets that already exist in C will be seen to already exist because their IDs will already be present. This is why changes are immutable. If you could modify a changeset it's ID would change (The ID is a hash of the changeset contents). Mercurial then (correctly) sees it as a different changeset. By keeping the changesets immutable we can be sure that their hash will be the same whichever repo they come from.
pull = copy changesets from over there that have IDs I haven't seen
push = copy changesets to over there that have IDs they haven't seen
Will there be a conflict
No
or it will merge successfully the changes A -> local -> C with changes A -> C.
No merge takes place because they are the same changes. It just sees that it already has them.
Merges don't happen unless you want to combine the changes from two parallel sets of changes, and not unless you explicitly ask for it.
As long as the changeset are identical, i.e. they have the same id hash. Mercurial considers them to be the same.
When you modify changesets with rebase or mq commands, the id hashs change and mercurial might place two very similar changesets into a repository.

CVS: Tracking all the changes in a branch

Say, I have three branches, Branch b0, b1 and b2.
b1 is forked some years back from b0 and b2 recently from b1.
All changes done on branch b2 are done by me and another developer, now what I need to do is to merge all my changes to branch b0.
I see two ways of doing it:
Manually copy-paste code (as code additions are quite independent) or
Create a diff file of Current state of branch b2 to the state where b2 was forked from b1. Apply the patch to b0, and manually handle conflicts.
2nd way seems more feasible to me, the only problem I have is how do I create a diff of all changes done on a branch?
Any other better way of doing this are also welcome.
Thanks,

Difference between Revert and Update in Mercurial

I'm just getting started with Mercurial, and I've come across something which I don't understand.
I made changes to several files, and now I want to undo all the changes I made to one of them (i.e. go back to my last commit for one specific file).
As far as I can see, the command I want is revert.
In the page I linked to, there is the following statement:
This operation however does not change
the parent revision of the working
directory (or revisions in case of an
uncommitted merge). To undo an
uncomitted merge, you can use "hg
update -C -r." which will reset the
parents to the first parent.
I don't understand the difference between the two (hg revert vs. hg update -C -r). Can anyone enlighten me as to the difference? And in my case, do I really want the revert or the update to go get rid of the changes I made to the file?
The first difference is revert can work on a subset of the working copy while update works on the whole working copy. the other difference is in what happens when you want to go back to a version other than the last committed one.
if we have revisions (caps are committed, lower case are changes in the working copy, parent revision is C )
A-B-C-d
update -C -r B will give you
A-B-C
with your working copy set to B, any changes will result in branching from B (parent revision set to B)
A-B-C
\e
revert -r B will give you
A-B-C-b'
where b' is a set of changes which undoes everything in the intermediate committed changes, in this case it undoes all of C. any changes now just join the b' set (parent revision left unchanged at C)