Merge repositories A and B into a separate repository C as separate folders - github

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

Related

Merging multiple feature branches into master without additional commit message?

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)

In Mercurial, move old file to location, move new files into old file location with same names

This should be simple, but something isn't quite right. Here is my scenario and then I'll give a brief overview of the commands I'm using. It helps to know that we have 3 specific dev areas, Live, Staging, and of course our own local dev areas.
I developed a new "beta" area of my site which has gone live and had appropriate testing. Now I'm ready to move it from a beta directory, to where it really should be and move out the old. When I do it locally, it seems fine, but when I try to merge my local branch into the staging branch, it doesn't seem to map the files correctly, and gives me a bunch of those use (c)hanged version, (d)elete, or leave (u)nresolved? prompts. The problem comes when my old directory has files that are named the same as the beta directory (like index.php for instance). Here's a quick example of what I mean:
currentDir/index.php
currentDir/update.php
currentDir/another_file.php
currentDir-beta/index.php
currentDir-beta/update.php
currentDir-beta/a_new_file.php
currentDir-beta/another_new_file.php
This is my process.
# creates a new branch from the live branch
hg branch new-branch-name
# move the current directory somewhere else
hg mv currentDir/* currentDir-old/
# commit...
hg com -m "moved current to -old"
# everything is fine up to this point
# move the beta directory to where the old one was
hg mv currentDir-beta/* currentDir/
# when I run hg st, it only shows that files are being removed from the -beta directory and added to the new/old directory
# commit
hg com -m "moved -beta to currentDir"
# when this commits is when the problems start happening.
# At this point when I run this next command, it shows that
# currentDir/index.php and other common files are now "modified" instead of "added"
hg st --rev "max(ancestors('new-branch-name') and branch(live)):'new-branch-name'"
# then try to merge to staging
hg up staging
hg merge new-branch-name
# errors happen with "common" file names like index.php. It treats them as though they were only modified instead of added.
Even if I ignored the above "modified" quirk, when I go to merge this new branch into the staging branch with other changes programmers have done, it complains that "local has this which remote deleted". I really wouldn't care with most of this as I could just throw this live and then any new branches would have this change. The thing I do care about is that any work done in the currentDir-beta folder on those "common" files from other programmers will no longer map to the new location. I can copy/paste the code and commit it, but it basically means that those branches are hosed as it pertains to keeping the changes other programmers did on those common files. To give you an example of what I mean, when I merge and type hg st it might look something like this.
M currentDir/index.php
M currentDir/update.php
M currentDir/a_new_file.php # why is this M? It should be A right?
M currentDir/another_new_file.php # why is this M? It should be A right?
M currentDir-old/another_file.php # why is this M? It should be A right?
R currentDir/another_file.php
R currentDir-beta/index.php
R currentDir-beta/update.php
R currentDir-beta/a_new_file.php
R currentDir-beta/another_new_file.php
Any suggestions on how to get around this? My goal is to make it so existing code changes that took place in currentDir-beta are "forwarded" to currentDir/ in the staging environment. All the other "not common" file changes are mapped, just not these common files.
UPDATE
Forgot to mention, I'm using Mercurial 3.9 on macOS Sierra.
I don't know
Your version of Mercurial
OS
but on my Win-box with Mercurial-3.9.1 my impressions (and results) differ
Clean initial state (folders shortened due to lazyness)
>hg st -A
C Current-beta\a_new_file.php
C Current-beta\another_new_file.php
C Current-beta\index.php
C Current-beta\update.php
C Current\another_file.php
C Current\index.php
C Current\update.php
First rename
>hg mv Current Current-Backup
moving Current\another_file.php to Current-Backup\another_file.php
moving Current\index.php to Current-Backup\index.php
moving Current\update.php to Current-Backup\update.php
...commit details skipped...
Second rename
>hg mv Current-beta Current
moving Current-beta\a_new_file.php to Current\a_new_file.php
moving Current-beta\another_new_file.php to Current\another_new_file.php
moving Current-beta\index.php to Current\index.php
moving Current-beta\update.php to Current\update.php
and working directory after it (as expected)
>hg st
A Current\a_new_file.php
A Current\another_new_file.php
A Current\index.php
A Current\update.php
R Current-beta\a_new_file.php
R Current-beta\another_new_file.php
R Current-beta\index.php
R Current-beta\update.php
...commit details skipped...
If you want to see how it was recorded by Mercurial: I used such slightly puzzling at first glance log for better interpreting of output
hg log -T "{rev}:{node|short}\n{if(file_adds,'\tAdded: {join(file_adds,', ')}\n')}{if(file_copies,'\tCopied: {join(file_copies,', ')}\n')}{if(file_dels,'\tDeleted: {join(file_dels,', ')}\n')}{if(file_mods,'\tModified: {join(file_mods,', ')}\n')}\n"
and here it's result
2:98955fcb7e71
Added: Current/a_new_file.php, Current/another_new_file.php, Current/index.php, Current/update.php
Copied: Current/a_new_file.php (Current-beta/a_new_file.php), Current/another_new_file.php (Current-beta/another_new_file.php), Current/index.php (Current-beta/index.php), Current/update.php (Current-beta/update.php)
Deleted: Current-beta/a_new_file.php, Current-beta/another_new_file.php, Current-beta/index.php, Current-beta/update.php
1:61068c6ba8a7
Added: Current-Backup/another_file.php, Current-Backup/index.php, Current-Backup/update.php
Copied: Current-Backup/another_file.php (Current/another_file.php), Current-Backup/index.php (Current/index.php), Current-Backup/update.php (Current/update.php)
Deleted: Current/another_file.php, Current/index.php, Current/update.php
0:454486bc43e5
Added: Current-beta/a_new_file.php, Current-beta/another_new_file.php, Current-beta/index.php, Current-beta/update.php, Current/another_file.php, Current/index.php, Current/update.php
As you can see - no edits ("Modified") at all (and here log /per changeset/ is more correct than aggregated status)
PS: I couldn't see on the fly purpose of your revset in hg st and necessity of branching+merging
PPS: OK, I saw
>hg st --rev "0:"
M Current\index.php
M Current\update.php
A Current-Backup\another_file.php
A Current-Backup\index.php
A Current-Backup\update.php
A Current\a_new_file.php
A Current\another_new_file.php
R Current-beta\a_new_file.php
R Current-beta\another_new_file.php
R Current-beta\index.php
R Current-beta\update.php
R Current\another_file.php
Aggregated results in considering only the boundary conditions is (correctly, technically speaking) modified files for files 1) in the same location 2) with the same name 3) and with changed content

Change the departure point of a branch in Egit

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.

Workflow to synchronise Mercurial repositories via email with bundles

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)

how push a bunch on changes as single one in mercurial

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