I have the following situation :
branch A| branch master
| |
| |
| | branch B
| | |<------B2
| |___|<------B1
| |<--------some commits (*) in B and in master, but not in A
\___|
|
I created a branch B from master but that was a mistake, I should have created B from A, and not from master.
So my initial thought was to Rebase B onto A.
The problem with this approach is that the commits (*) are also rebased in B, which I don't want.
So I wanted to use "cherry-picking" to duplicate the commits from B (after the divergent point with master) into A.
But since Egit uses Interactive Rebase to do that, the problem is the same. The commits (*) are present in A.
Basically, I want to apply the functional changes introduced by B1 and B2 in the branch A, without the changes introduced by (*).
The only solution that I found is to manually 'create Patch' for B1 and apply it in A, and then 'create Patch' for B2, and apply it in A.
Is there another way to easily do what I want to achieve using Egit ?
In the command line:
git rebase --onto a last-ignored-commit b
or
git rebase --onto a first-wanted-commit~ b
If you are already on branch-b you do not need to specify that as the last argument. Both commands evaluate to the same thing. The tilde (~) means find the first parent. Therefore last-ignored-commit and first-wanted-commit~ both refer to the same commit.
Related
Currently I have two projects/repos A and B:
A --- B
I would like to merge these into one repo C as subdirectories with the commit history and everything merged, only main branches need to be merged. I want to end up with something like this:
C
|
/ \
A B
Suppose you have a master branch:
A--B--C
Feature 1 Branch:
A--B--C--D
Feature 2 Branch:
A--B--C--E
When we do a git merge Feature1 into master, it merges fine, however when trying to merge Feature2, we are presented with vi asking us to enter a commit message for the merge. Is there a way to merge these branches without having extra merge commits? They share the same history from master aside from the feature commit.
The final history on master should look like:
A--B--C--D--E
depending on which commit date (D or E) is first
Background
In git, the history is built up by recording the parents of each commit - generally, a "normal" commit has one parent, and a "merge commit" has two, but there can actually be any number of parents, including zero.
Every commit is identified by a hash of both its content and its metadata - that includes who committed it and when, and its list of parents. You can't change any part of that data without getting a new commit hash, so all commits are effectively immutable.
A "branch" in git actually just points to a single commit, and git follows history backwards from there.
The scenario, as git sees it
Each commit points at its parent or parents, and each branch points at a commit.
Note that the angles on this graph don't mean anything, they're just to lay it out in 2D.
+--D <--(feature1)
v
A <--B <--C <--(master)
^
+--E <--(feature2)
The fast-forward merge
By default, git will "fast-forward" history whenever it can. What this means is that it just moves the branch pointer without touching any commits at all.
This is what you see when you merge your first feature branch: git fast-forwards the "master" pointer to point at commit D, and leaves everything else alone:
+--(master)
V
+--D <--(feature1)
v
A <--B <--C
^
+--E <--(feature2)
Which (remembering that angles don't mean anything) we can also draw like this:
A <--B <--C <--D <--(master, feature1)
^
+--E <--(feature2)
The merge commit
When we come to merge the second feature branch, we can't just fast-forward any more - pointing "master" at commit E would lose commit D. So git's other option is to create a "merge commit" - a commit with more than one parent. The pointer for "master" can then point to this new commit.
This is why you're prompted for a message on your second merge, because git is creating a new commit (let's call it "M2") so both D and E will be in its history:
A <--B <--C <--D <--(feature1)
^ ^
| |
| M2 <--(master)
| |
| v
+----E <--(feature2)
Which we might also draw like this:
+--(feature1)
v
A <--B <--C <--D <--M2 <--(master)
^ |
| v
+---------E <--(feature2)
Note that we could have forced git to do this with the previous merge as well, using git merge --no-ff, which would have given us something more like this:
+----D <--(feature1)
| ^
v |
A <--B <--C <--M1 <--M2 <--(master)
^ |
| v
+----------E <--(feature2)
Rebase
So, how do we make a history that looks like this?
A <--B <--C <--D <--E <--(master)
On the face of it, we can't: E's parent is recorded as C, not D, and commits are immutable. But what we can do is create a new commit which looks like E but has D as its parent. This is what git rebase does.
After fast-forwarding feature 1, we had this:
A <--B <--C <--D <--(master, feature1)
^
+--E <--(feature2)
If we now git rebase master feature2, git will create a new version of all commits reachable from feature2 which aren't already reachable from master. It will try to create commits which apply the same changes, and by default copy commit messages and even the original author and timestamp, but they'll have new parents.
It will then point feature2 at these new commits; in our case, the result will look something like this:
A <--B <--C <--D <--(master, feature1)
^ ^
| +--E2 <--(feature2)
|
+--E
The original commit E is now not reachable from any branch, and will be cleaned up. But now we can avoid the merge commit: the new commit E2 is in a position where we can fast-forward master again:
A <--B <--C <--D <--(feature1)
^
+--E2 <--(feature2)
|
+ <--(master)
To redraw:
+--(feature1)
v
A <--B <--C <--D <--E2 <--(master, feature2)
I have an issue while using Bazaar and I would like to get the best practices to fix it.
Here is the context:
In the trunk of our project, one wrongly merged a branch (BranchA) in the trunk but he used revert on most files while keeping the merge info (so from bazaar BranchA was effectively merged in trunk, preventing to REALLY merge it later).
The merged revision was committed as r4.
After this (undesired) merge, many devs committed right after (r5 & r6).
So my question is : how to undo this wrong merge ? (while keeping the commits after).
I tried to uncommit back to r3 and merge each rev from r4 to R6 (omitting r4)
I tried 'reverse cherry pick' r4 ... but the BranchA merge information may still memorized.
trunk
|
r6
|
r5
| branchA
| |
r4 ---+
| |
| r2.2
| |
r3 r2.1
| |
r2 ---+
|
r1
If you have a solution or some clue to fix it, please share it !
(for example can REBASE command help here?)
Since information was lost due to the reverts in the merge that has gone wrong, there won't be a 100% fix. Your best bet is to create a new branch off of r3 in the trunk and cherry pick r5 and r6, like this:
cd /path/to/repo
bzr branch trunk -r3 branchX
cd branchX
bzr merge -c5 ../trunk
bzr commit -m "cherry picked r5: $(bzr log --line -r5 ../trunk)"
bzr merge -c6 ../trunk
bzr commit -m "cherry picked r6: $(bzr log --line -r6 ../trunk)"
Or, before cherry picking r5, you might want to merge from branchA the "right way".
The rebase command won't help you here. The purpose of rebasing is basically to reorder revisions: whether you merge or rebase, you end up with the same content in the files, only the revision history graph will have a different shape. The purpose of rebase is usually to make a history graph look flat, which can be neat, but it's only cosmetics, in terms of content the end result is the same.
I have two directories on two different computers - machine A (Windows) and machine B (OSX) - and I want to keep the two directories via Mercurial in sync. [*]
The restriction is that the two machines are not connected via LAN/WAN; the only way to move data between them is via email. So I thought emailing Mercurial bundles as deltas could do the trick.
My current workflow is roughly this (using a local tag lcb for the latest change bundle):
Say I work on machine A. At the end of the day I do:
hg commit -A -m "changes 123"
hg bundle --base lcb bundle_123.hg
hg tag --local -f lcb --rev tip
finally then I email that bundle to machine B.
Then sitting at machine B I do
hg unbundle bundle_123.hg
hg merge
hg commit -A -m "remote changes 123"
hg tag --local -f lcb --rev tip
Now I'm working on machine B and at the end of the day I do what's listed under 1., but on machine B. And the cycle continues...
However, I'm worry this system is not robust enough:
In-between changes: What happens when after creating a bundle (Step 1) and before applying it remotely (Step 2) a changes occurrs on the remote machine B? I had a case where it just overwrote the changes with the new bundle without conflict warning or merge suggestion.
Double-applying of bundle: What happens when by accident a bundle is applied twice? Would be needed to record the applied bundles somehow with local tags?
Or is there another better workflow to transfer Mercurial deltas via email?
[*] From the answer to a superuser question I figured that Mercurial might be the most feasible way to do this.
In-between changes: What happens when after creating a bundle (Step 1) and before applying it remotely (Step 2) a changes occurs on the remote machine B? I had a case where it just overwrote the changes with the new bundle without conflict warning or merge suggestion.
If a change is made on machine B, then this change will have been made in parallel with the changes you bundled from machine A. It doesn't really matter if the changes are made before or after you create the bundle (time-wise), it only matters that the changes on machine B don't have the head from machine A as their ancestor.
In other words, the world looks like this when the two machines are in sync:
A: ... [a]
B: ... [a]
You then create some new commits on machine A:
A: ... [a] --- [b] --- [c]
B: ... [a]
You bundle using [a] as base, so you get a bundle with [b] and [c]. Let us now say that someone (perhaps yourself) makes a commit on machine B:
A: ... [a] --- [b] --- [c]
( bundled )
B: ... [a] --- [x]
So far nothing has been exchanged between the two repositories, so this is just a normal case of people working in parallel. This is the norm in a distributed version control system — people working in parallel is that creates the need for merge commits.
The need for a merge is not evident in either repository at this point, they both have linear histories. However, when you unbundle on machine B, you see the divergence:
A: ... [a] --- [b] --- [c]
( bundled )
B: ... [a] --- [x]
\
[b] --- [c]
( unbundled )
It is helpful to realize that hg unbundle is exactly like hg pull, except that it can be done offline. That is, the data stored in a bundle is really just the data that hg pull would have transferred if you had had an online connection between the two repositories.
You would now proceed by merging the two heads [x] and [c] to create [y] on machine B:
A: ... [a] --- [b] --- [c]
B: ... [a] --- [x] --- [y]
\ /
[b] --- [c]
on machine B your last bundle was created with [a] as a base. However, you also know that machine A has commit [c], so you can specify that as an additional base if you like:
$ hg bundle --base a --base c stuff-from-machine-b.hg
That will put [x] and [y] into the bundle:
bundle: (a) --- [x] --- [y]
/
(c)
Here I use (a) and (c) to denote the required bases of the bundle. You can only unbundle this bundle if you have both [a] and [c] in your repository. If you leave out the second base (only use [a]), you will also bundle [b] and [c]:
bundle: (a) --- [x] --- [y]
\ /
[b] --- [c]
Here you included everything except [a] in the bundle. Bundling too much is okay, as we will see next.
Double-applying of bundle: What happens when by accident a bundle is applied twice? Would be needed to record the applied bundles somehow with local tags?
Applying a bundle twice is exactly like running hg pull twice: nothing happens the second time. When unbundling, Mercurial looks in the bundle and imports the missing changesets. So if you unbundle twice, there is nothing to do the second time.
Initial state
A>hg log --template "{rev}:{node|short} \"{desc}\" - files: {files}\n"
2:415231dbafb8 "Added C" - files: C.txt
1:6d9709a42687 "Added B" - files: B.txt
0:e26d1e14507e "Initial data" - files: .hgignore A.txt
B>hg log --template "{rev}:{node|short} \"{desc}\" - files: {files}\n"
1:72ef13990d0d "Edited A" - files: A.txt
0:e26d1e14507e "Initial data" - files: .hgignore A.txt
i.e:
Identical repos diverged at revision 1 at both sides: independent changes appeared
Test for case 1 - parallel changes
72ef13990d0d in B doesn't interfere with 6d9709a42687:415231dbafb8 in A
A>hg bundle --base e26d1e14507e ..\bundle1-2.hg
2 changesets found
B>hg pull ..\bundle1-2.hg
pulling from ..\bundle1-2.hg
searching for changes
adding changesets
adding manifests
adding file changes
added 2 changesets with 2 changes to 2 files (+1 heads)
(run 'hg heads' to see heads, 'hg merge' to merge)
because B had own child for e26d1e14507e, pulling from bundle added additional head (and anonymous branch for changesets from A)
B>hg glog --template "{rev}:{node|short} \"{desc}\" - files: {files}\n"
o 3:415231dbafb8 "Added C" - files: C.txt
|
o 2:6d9709a42687 "Added B" - files: B.txt
|
| # 1:72ef13990d0d "Edited A" - files: A.txt
|/
o 0:e26d1e14507e "Initial data" - files: .hgignore A.txt
Test for case 2 - applying bundle twice
I know apriori, that existing in repo changesets will not be pulled again (and prefer unified style of hg pull from bundle instead of hg unbundle), but show it
B>hg pull ..\bundle1-2.hg
pulling from ..\bundle1-2.hg
searching for changes
no changes found
Additional benefit from pull's behavior - you can don't worry about moving base changeset for bundle and always use one, oldest point of divergence - it will (slightly) increase size of bundle (slightly - because by default bundle is bzip2-compressed archive), but also it will it guarantees the inclusion of all child changesets into bundle and pulling all missing (and only missing) changesets in destination repository
And, it any case, even unbundle the same bundle twice will not have any backfires.
Same bundle in same repo B, attempt to unbundle already pulled bundle
B>hg unbundle ..\bundle1-2.hg
adding changesets
adding manifests
adding file changes
added 0 changesets with 0 changes to 2 files
(run 'hg update' to get a working copy)
As I understand one of the main advantages of distributed revision control system like Mercurial is that you should not worry about breaking something in your super-important main repo (which is used by quite a lot other developers), and do all you work and researches in your personal remote clone until you understand that everything stable and you can push your work back.
And hence I got my question: if it possible to push back not all your changes history (with several revisions which you made for yourself), but only one which is actually diff between your repo and current state of master.
One example:
hg init master-repo; cd master-repo
echo -e 'Important file\nWith Bug!' > file
hg commit -A -m "initial commit"
cd ..; hg clone master-repo hotfix-repo; cd hotfix-repo
echo "fix1" >> file
hg commit -m "first attempt to fix bug"
echo "fix2" >> file
hg commit -m 'Fixed it!'
Now (possibly after pull and merge with newest master-repo' changes) I want to push back only one changeset containing all changes that I've done without my local commits history.
One possible solution is to create one more clone then use diff/patch between two clones to extract/apply changes from first one and commit them all at once in second repo. Then do push as in normal case. But is it possible to so using only mercurial commands?
Thanks in forward!
Opinions differ on whether or not it's good to collapse trial-and-error changesets into a single changeset before pushing:
Pros: you avoid having changesets in your history where your test suite fails — those changesets are bad for hg bisect and add noise.
Con: you cannot collapse changesets that you have published to other repositories — doing so would only rewrite your local changesets and you would then have to clean up the other repositories manually.
Technically, it's perfectly safe to collapse a set of changesets into a single changeset before pushing. You start with
... a --- b --- c --- x --- y --- z
and you rewrite this into
... a --- b --- c --- w
where w has exactly the same repository state as z had (but a different parent changeset, obviously). There are no merges here (and hence no merge conflicts) so it cannot fail.
After rewriting, you can pull and merge with the upstream (d and e):
... a --- b --- c --- w --- v
\ /
d ----- e
You need an extension to do any kind of history rewriting. Here I would suggest one of:
Collapse extension: as the name implies, this extension is dedicated to collapsing changesets.
Histedit extension: full-fledged history editing, but the fold command let's you collapse changesets.
Rebase extension: this standard extension can move changesets around and collapse them at the same time. In the example above, it would move x --- y --- z after e:
... a --- b --- c --- d --- e --- x' --- y' --- z'
You can then optionally collapse x' to z':
... a --- b --- c --- d --- e --- w'
Compared to just collapsing x to z, rebasing does involve merges so it can fail.
Intro
I think, rewrite history and send/get "polished" changesets (in Git-boys style) is, in common, bad idea - history is history, it have own value.
After all, for "mainline" branch (you use branches, isn't it), all your changes will be presented as one mergeset, regardless of changesets count in branch
Short answer
No. In Mercurial you pull/push and get&accept full set of changesets, which created difference in repo history
Long answer
Somehow you can, by rewting your history of changesets before exchange to "other side". Just remember - each changeset represent not new state of object, but diff between old and current (if describe it shortly), thus - by ordinary removing changests you can get wrong final result.
Anyway, you have a lot of ways to rewrite own history (as extensions):
Collapse
History Edit
MQ (with mq-patches per se, folding changesets in mq-patch and splitting the same way)
Maybe some others, unknown for me