git merge after renaming of all files The Next CEO of Stack OverflowHow to remove local (untracked) files from the current Git working tree?How to resolve merge conflicts in GitWhat is the difference between 'git pull' and 'git fetch'?How to undo 'git add' before commit?How do I undo the most recent commits in Git?How do I force “git pull” to overwrite local files?How do I delete a Git branch both locally and remotely?How to revert a Git repository to a previous commitHow to use git merge --squash?How do I rename a local Git branch?
Anatomically Correct Strange Women In Ponds Distributing Swords
Solution of this Diophantine Equation
When airplanes disconnect from a tanker during air to air refueling, why do they bank so sharply to the right?
Whats the best way to handle refactoring a big file?
What size rim is OK?
Is it a good idea to use COLUMN AS (left([Another_Column],(4)) instead of LEFT in the select?
Why does standard notation not preserve intervals (visually)
What does "Its cash flow is deeply negative" mean?
What is the purpose of the Evocation wizard's Potent Cantrip feature?
How to make a software documentation "officially" citable?
How easy is it to start Magic from scratch?
Any way to transfer all permissions from one role to another?
Why is there a PLL in CPU?
What makes a siege story/plot interesting?
How to write papers efficiently when English isn't my first language?
What does this shorthand mean?
MAZDA 3 2006 (UK) - poor acceleration then takes off at 3250 revs
How can I get through very long and very dry, but also very useful technical documents when learning a new tool?
What can we do to stop prior company from asking us questions?
WOW air has ceased operation, can I get my tickets refunded?
How do scammers retract money, while you can’t?
% symbol leads to superlong (forever?) compilations
Text adventure game code
Is it my responsibility to learn a new technology in my own time my employer wants to implement?
git merge after renaming of all files
The Next CEO of Stack OverflowHow to remove local (untracked) files from the current Git working tree?How to resolve merge conflicts in GitWhat is the difference between 'git pull' and 'git fetch'?How to undo 'git add' before commit?How do I undo the most recent commits in Git?How do I force “git pull” to overwrite local files?How do I delete a Git branch both locally and remotely?How to revert a Git repository to a previous commitHow to use git merge --squash?How do I rename a local Git branch?
There are other answers regarding handling merge
for a rename, but my case is complicated enough that I thought it warranted a separate question.
We have a git project that originally consisted of 20+ repositories. We used a wrapper script to handle many of the standard git operations. Because we are now moving to GitHub, we cannot handle the project this way.
So, we moved all repositories into a single repository, essentially using the method described on saintgimp. This, of course, means that all files have now been renamed, but the SHAs are identical historically.
OK, so now I want to merge branch source
into branch target
, noting that I made sure the two were in sync right before the final cutover. My first attempt, using git merge <source>
caused thousands of conflicts, complaints about files that were changed/deleted on one side or the other, etc.
Then I found a gem on the Advanced Merging page:
If you want to do something like this but not have Git even try to
merge changes from the other side in, there is a more draconian
option, which is the “ours” merge strategy. This is different from the
“ours” recursive merge option.
Ah, this sounds like what I need. OK, I performed the following:
$ git merge -s ours SHA
where SHA
is the last commit from the reunification. In other words, I want all history, up to and including SHA
, to be considered already merged into target
. My hope was that this would be a one-time merge and would fix all future merges.
Now, when I try to merge the first new commit from source
, the effect is correct, but I continue to get the following warning:
[user@host src] git merge --no-commit next_unmerged_commit
Auto-merging /path/to/file/changed/foo.c
warning: inexact rename detection was skipped due to too many files.
warning: you may want to set your merge.renamelimit variable to at least 5384 and retry the command.
Automatic merge went well; stopped before committing as requested
And, in fact, if I set renamelimit
to 10000, the next merge (call it B
) is performed without warning, but at a cost of much slower performance. Once again, a one-time cost is acceptable and I'll pay that cost if my subsequent merges are made normal again.
The next merge, C
, where I use the default renamelimit
, again gives the warning.
So, finally, my question: How can I convince git that the target
branch is in sync with source
so that it will stop trying to reach back in history before the reunification? I want to be able to merge without an increased renamelimit
, due to the performance degradation.
git merge git-mv
|
show 3 more comments
There are other answers regarding handling merge
for a rename, but my case is complicated enough that I thought it warranted a separate question.
We have a git project that originally consisted of 20+ repositories. We used a wrapper script to handle many of the standard git operations. Because we are now moving to GitHub, we cannot handle the project this way.
So, we moved all repositories into a single repository, essentially using the method described on saintgimp. This, of course, means that all files have now been renamed, but the SHAs are identical historically.
OK, so now I want to merge branch source
into branch target
, noting that I made sure the two were in sync right before the final cutover. My first attempt, using git merge <source>
caused thousands of conflicts, complaints about files that were changed/deleted on one side or the other, etc.
Then I found a gem on the Advanced Merging page:
If you want to do something like this but not have Git even try to
merge changes from the other side in, there is a more draconian
option, which is the “ours” merge strategy. This is different from the
“ours” recursive merge option.
Ah, this sounds like what I need. OK, I performed the following:
$ git merge -s ours SHA
where SHA
is the last commit from the reunification. In other words, I want all history, up to and including SHA
, to be considered already merged into target
. My hope was that this would be a one-time merge and would fix all future merges.
Now, when I try to merge the first new commit from source
, the effect is correct, but I continue to get the following warning:
[user@host src] git merge --no-commit next_unmerged_commit
Auto-merging /path/to/file/changed/foo.c
warning: inexact rename detection was skipped due to too many files.
warning: you may want to set your merge.renamelimit variable to at least 5384 and retry the command.
Automatic merge went well; stopped before committing as requested
And, in fact, if I set renamelimit
to 10000, the next merge (call it B
) is performed without warning, but at a cost of much slower performance. Once again, a one-time cost is acceptable and I'll pay that cost if my subsequent merges are made normal again.
The next merge, C
, where I use the default renamelimit
, again gives the warning.
So, finally, my question: How can I convince git that the target
branch is in sync with source
so that it will stop trying to reach back in history before the reunification? I want to be able to merge without an increased renamelimit
, due to the performance degradation.
git merge git-mv
I note that you used--no-commit
in the second example. If you did that in the first (git merge -s ours
), that would explain the problem. If not, it might help to provide a lot more detail on what you did to get into this situation (the first link you posted talks about using subtree merging so more detail could be important).
– torek
Mar 21 at 18:13
My question: are the new branches you're going to want to commit in the future, branches that were created before or after the initial mega-merge? In the situation you describe, I would expect such future merges to be fast, only merges from a branch that predates the mega merge to be slow. Does your workflow allow giving up on pre-mega-merge branches, or at least progressively phasing them out?
– joanis
Mar 21 at 19:52
Second question, geared towards answering yours more directly: doesgit merge-base master target
point to a commit before or after the mega-merge? Third question: are the file names insource
andtarget
the same, or pre & post renaming?
– joanis
Mar 21 at 19:55
@torek The use of--no-commit
was arbitrary and was not used in the initialgit merge -s ours
merge.
– tanager
Mar 22 at 12:16
@joanis Bothsource
andtarget
were created before the mega-merge. Yes, we will eventually give up onsource
, but not for a few months at least.
– tanager
Mar 22 at 12:29
|
show 3 more comments
There are other answers regarding handling merge
for a rename, but my case is complicated enough that I thought it warranted a separate question.
We have a git project that originally consisted of 20+ repositories. We used a wrapper script to handle many of the standard git operations. Because we are now moving to GitHub, we cannot handle the project this way.
So, we moved all repositories into a single repository, essentially using the method described on saintgimp. This, of course, means that all files have now been renamed, but the SHAs are identical historically.
OK, so now I want to merge branch source
into branch target
, noting that I made sure the two were in sync right before the final cutover. My first attempt, using git merge <source>
caused thousands of conflicts, complaints about files that were changed/deleted on one side or the other, etc.
Then I found a gem on the Advanced Merging page:
If you want to do something like this but not have Git even try to
merge changes from the other side in, there is a more draconian
option, which is the “ours” merge strategy. This is different from the
“ours” recursive merge option.
Ah, this sounds like what I need. OK, I performed the following:
$ git merge -s ours SHA
where SHA
is the last commit from the reunification. In other words, I want all history, up to and including SHA
, to be considered already merged into target
. My hope was that this would be a one-time merge and would fix all future merges.
Now, when I try to merge the first new commit from source
, the effect is correct, but I continue to get the following warning:
[user@host src] git merge --no-commit next_unmerged_commit
Auto-merging /path/to/file/changed/foo.c
warning: inexact rename detection was skipped due to too many files.
warning: you may want to set your merge.renamelimit variable to at least 5384 and retry the command.
Automatic merge went well; stopped before committing as requested
And, in fact, if I set renamelimit
to 10000, the next merge (call it B
) is performed without warning, but at a cost of much slower performance. Once again, a one-time cost is acceptable and I'll pay that cost if my subsequent merges are made normal again.
The next merge, C
, where I use the default renamelimit
, again gives the warning.
So, finally, my question: How can I convince git that the target
branch is in sync with source
so that it will stop trying to reach back in history before the reunification? I want to be able to merge without an increased renamelimit
, due to the performance degradation.
git merge git-mv
There are other answers regarding handling merge
for a rename, but my case is complicated enough that I thought it warranted a separate question.
We have a git project that originally consisted of 20+ repositories. We used a wrapper script to handle many of the standard git operations. Because we are now moving to GitHub, we cannot handle the project this way.
So, we moved all repositories into a single repository, essentially using the method described on saintgimp. This, of course, means that all files have now been renamed, but the SHAs are identical historically.
OK, so now I want to merge branch source
into branch target
, noting that I made sure the two were in sync right before the final cutover. My first attempt, using git merge <source>
caused thousands of conflicts, complaints about files that were changed/deleted on one side or the other, etc.
Then I found a gem on the Advanced Merging page:
If you want to do something like this but not have Git even try to
merge changes from the other side in, there is a more draconian
option, which is the “ours” merge strategy. This is different from the
“ours” recursive merge option.
Ah, this sounds like what I need. OK, I performed the following:
$ git merge -s ours SHA
where SHA
is the last commit from the reunification. In other words, I want all history, up to and including SHA
, to be considered already merged into target
. My hope was that this would be a one-time merge and would fix all future merges.
Now, when I try to merge the first new commit from source
, the effect is correct, but I continue to get the following warning:
[user@host src] git merge --no-commit next_unmerged_commit
Auto-merging /path/to/file/changed/foo.c
warning: inexact rename detection was skipped due to too many files.
warning: you may want to set your merge.renamelimit variable to at least 5384 and retry the command.
Automatic merge went well; stopped before committing as requested
And, in fact, if I set renamelimit
to 10000, the next merge (call it B
) is performed without warning, but at a cost of much slower performance. Once again, a one-time cost is acceptable and I'll pay that cost if my subsequent merges are made normal again.
The next merge, C
, where I use the default renamelimit
, again gives the warning.
So, finally, my question: How can I convince git that the target
branch is in sync with source
so that it will stop trying to reach back in history before the reunification? I want to be able to merge without an increased renamelimit
, due to the performance degradation.
git merge git-mv
git merge git-mv
edited Mar 22 at 12:45
tanager
asked Mar 21 at 16:30
tanagertanager
11118
11118
I note that you used--no-commit
in the second example. If you did that in the first (git merge -s ours
), that would explain the problem. If not, it might help to provide a lot more detail on what you did to get into this situation (the first link you posted talks about using subtree merging so more detail could be important).
– torek
Mar 21 at 18:13
My question: are the new branches you're going to want to commit in the future, branches that were created before or after the initial mega-merge? In the situation you describe, I would expect such future merges to be fast, only merges from a branch that predates the mega merge to be slow. Does your workflow allow giving up on pre-mega-merge branches, or at least progressively phasing them out?
– joanis
Mar 21 at 19:52
Second question, geared towards answering yours more directly: doesgit merge-base master target
point to a commit before or after the mega-merge? Third question: are the file names insource
andtarget
the same, or pre & post renaming?
– joanis
Mar 21 at 19:55
@torek The use of--no-commit
was arbitrary and was not used in the initialgit merge -s ours
merge.
– tanager
Mar 22 at 12:16
@joanis Bothsource
andtarget
were created before the mega-merge. Yes, we will eventually give up onsource
, but not for a few months at least.
– tanager
Mar 22 at 12:29
|
show 3 more comments
I note that you used--no-commit
in the second example. If you did that in the first (git merge -s ours
), that would explain the problem. If not, it might help to provide a lot more detail on what you did to get into this situation (the first link you posted talks about using subtree merging so more detail could be important).
– torek
Mar 21 at 18:13
My question: are the new branches you're going to want to commit in the future, branches that were created before or after the initial mega-merge? In the situation you describe, I would expect such future merges to be fast, only merges from a branch that predates the mega merge to be slow. Does your workflow allow giving up on pre-mega-merge branches, or at least progressively phasing them out?
– joanis
Mar 21 at 19:52
Second question, geared towards answering yours more directly: doesgit merge-base master target
point to a commit before or after the mega-merge? Third question: are the file names insource
andtarget
the same, or pre & post renaming?
– joanis
Mar 21 at 19:55
@torek The use of--no-commit
was arbitrary and was not used in the initialgit merge -s ours
merge.
– tanager
Mar 22 at 12:16
@joanis Bothsource
andtarget
were created before the mega-merge. Yes, we will eventually give up onsource
, but not for a few months at least.
– tanager
Mar 22 at 12:29
I note that you used
--no-commit
in the second example. If you did that in the first (git merge -s ours
), that would explain the problem. If not, it might help to provide a lot more detail on what you did to get into this situation (the first link you posted talks about using subtree merging so more detail could be important).– torek
Mar 21 at 18:13
I note that you used
--no-commit
in the second example. If you did that in the first (git merge -s ours
), that would explain the problem. If not, it might help to provide a lot more detail on what you did to get into this situation (the first link you posted talks about using subtree merging so more detail could be important).– torek
Mar 21 at 18:13
My question: are the new branches you're going to want to commit in the future, branches that were created before or after the initial mega-merge? In the situation you describe, I would expect such future merges to be fast, only merges from a branch that predates the mega merge to be slow. Does your workflow allow giving up on pre-mega-merge branches, or at least progressively phasing them out?
– joanis
Mar 21 at 19:52
My question: are the new branches you're going to want to commit in the future, branches that were created before or after the initial mega-merge? In the situation you describe, I would expect such future merges to be fast, only merges from a branch that predates the mega merge to be slow. Does your workflow allow giving up on pre-mega-merge branches, or at least progressively phasing them out?
– joanis
Mar 21 at 19:52
Second question, geared towards answering yours more directly: does
git merge-base master target
point to a commit before or after the mega-merge? Third question: are the file names in source
and target
the same, or pre & post renaming?– joanis
Mar 21 at 19:55
Second question, geared towards answering yours more directly: does
git merge-base master target
point to a commit before or after the mega-merge? Third question: are the file names in source
and target
the same, or pre & post renaming?– joanis
Mar 21 at 19:55
@torek The use of
--no-commit
was arbitrary and was not used in the initial git merge -s ours
merge.– tanager
Mar 22 at 12:16
@torek The use of
--no-commit
was arbitrary and was not used in the initial git merge -s ours
merge.– tanager
Mar 22 at 12:16
@joanis Both
source
and target
were created before the mega-merge. Yes, we will eventually give up on source
, but not for a few months at least.– tanager
Mar 22 at 12:29
@joanis Both
source
and target
were created before the mega-merge. Yes, we will eventually give up on source
, but not for a few months at least.– tanager
Mar 22 at 12:29
|
show 3 more comments
1 Answer
1
active
oldest
votes
This really isn't a very good answer as it's more about the script you used—or perhaps I should say, the script you didn't use, as your comment says that you used one based on the script to which you linked—but I'll show the rather tangled graph I get in a hypothetical script-conversion of some original repositories below. Note that this particular script leaves all the conversions with a merge base commit of, in essence, commit B
, and commit B
itself is empty.
Your question says:
now I want to merge branch
source
into branchtarget
, noting that I made sure the two were in sync right before the final cutover.
As you'll see below, all the new branches are named after the project that they came from—there's no clear way to map source
and target
onto, e.g., P or Q. But if you were to run:
git checkout P/master
git merge Q/master
after the process illustrated below, the merge base for this git merge
would be empty-commit-B
and the merge would go smoothly: Git would look at the commits I drew as D
and H
respectively, trace their ancestries, find commit B
as their merge base, and run two git diff
s:
git diff <hash-of-B> <hash-of-D> # what we did on P/master
git diff <hash-of-B> <hash-of-H> # what they did on H/master
The output of these git diff
s would say that every file is created from scratch, and all their names are different: everything in P/master
is named P/*
and everything in H/master
is named Q/*
. There would be no name collisions and the merge would complete on its own.
Clearly, that's not what you're doing, then. But what you are doing, and which commit is the merge base, remains mysterious. It looks like you're picking out two tip commits such that the merge base of the two tip commits is a commit that does have files, and those files are not yet renamed from base to tip.
The point of the script you linked is to set things up so that the merge bases of each of the unrelated projects is an empty commit. Probably, the thing to do after that script—or in place of that script, really—is to do one massive octopus merge of all the final commits (NB: this is untested, as is probably obvious):
git checkout P/master # just to be somewhere that's not master
git branch -d master # discard existing master branch name
git checkout --orphan master # arrange to create new master
git merge P/master Q/master R/master # make big octopus merge to marry all projects
The merge base of this octopus merge would again be commit B
, and the result would be one merge that brings all the projects in under their new project/*
names. The original repositories are now all mostly useless, though if there are new commits in them, you can fetch from them, add a renaming commit, and merge from the renaming commit (this would be easier if the importing script didn't delete the added remotes).
Observations on the workings of the linked script
I've never faced this particular problem, but the approach in the script seems like a not-unreasonable starting point. I'd probably do it a bit differently, not bothering with an empty merge base and using git read-tree
and git commit-tree
to build and create the end octopus merge. The main key is to add a rename commit at the end of each incoming project branch (P/*
, Q/*
, etc) in the sketch below.
The script seems to work this way. It has as inputs projects P, Q, R (URLs whose last component is treated a project name).
- Make empty repo.
Make two iniial commits:
A--B <-- master
Commit A has one file, commit B has no files (why not just
commit the empty tree as B? but never mind).
Loop, for all three projects. Here I have expanded the loop to view
what's happening.(loop iteration 1)
git remote add P <url>
andgit fetch P
(with--tags
!?). We're going to assume here that P has master and dev.A--B <-- master
P1-P2-...-Pm <-- origin/P/master
Pd <-- origin/P/devUse git ls-remote --heads to find names for commits in P, i.e.,
the same set of names we have inrefs/remotes/P/*
. (Assumes the
remote hsa not changed during fetch -- unwise but probably OK.)Loop over these names. Result again expanded in line for illustration...
Run
git checkout -b P/master master
. Effect:A--B <-- master, P/master
P1-P2-...-Pm <-- origin/P/master
Pd <-- origin/P/devRun
git reset --hard
for no apparent reason: no effect. Perhaps
this might have some effect on some later step.Run
git clean -d --force
for no apparent reason: no effect.Run
git merge --allow-unrelated-histories --no-commit remotes/P/master"
git commit -m ...`.
(does merge, but does not commit yet) and then run
Effect:A--B <-- master
-------C <-- P/master
/
P1-P2-...-Pm <-- origin/P/master
Pd <-- origin/P/devMaybe rename files, with somewhat squirrelly code (lines 160-180):
if project P has one top level directory named P, do nothing, otherwise
create directory named P (with no check to see if this fails) and
then in effect:git mv all-but-P P/
git commit -m "[Project] Move $sub_project files into sub directory"giving:
A--B <-- master
-------C--D <-- P/master
/
P1-P2-...-Pm <-- origin/P/master
Pd <-- origin/P/devNote that the
git mv
is given-k
so that it does nothing if
one of thegit mv
operations would have failed. However, except
for subdirectory P and.git
itself, all files in the top level
of the work-tree should be in the index and thegit mv
should
succeed unless one of them is namedP
(in which case, yikes!).I assume here that we did the mv, otherwise commit
D
does not exist.Repeat loop (see step 5) for
dev
. Rungit checkout -b P/dev master
:A--B <-- master, P/dev
-------C--D <-- P/master
/
P1-P2-...-Pm <-- origin/P/master
Pd <-- origin/P/devPresumably-ineffectual
git reset
andgit clean
again
as in steps 7 and 8. (This might do something if thegit mv
in step 10 went really badly?) Do a funky two step merge as in
step 9, giving:A--B <-- master
|
| -------C--D <-- P/master
/
P1-P2-...-Pm <-- origin/P/master
Pd <-- origin/P/dev
---E <-- P/devwhere the line down from B connects to the one up from E. The
graph has gotten rather out of hand at this point.Rename and commit as in step 10. I assume here that the
project isn't already in a subdirectory, in bothmaster
, as
already assumed, anddev
.A--B <-- master
|
| -------C--D <-- P/master
/
P1-P2-...-Pm <-- origin/P/master
Pd <-- origin/P/dev
---E--F <-- P/devReally ugly attempt to rename tags, at lines 190-207. This
should have been done at fetch time, using a clever refspec.
Whoever wrote this probably was not aware of annotated vs
lightweight tags. It is not clear to me whether this works
correctly and I did not look closely. Let's just assume no tags
for now.Remove remote P. This removes the
origin/P/*
names too,
but of course the commits stick around as they're retained by
the newP/*
branches:A--B <-- master
|
| -------C--D <-- P/master
/
P1-P2-...-Pm
Pd
---E--F <-- P/devRepeat outer loop (step 3) with remote Q. We'll add Q and
fetch (again with--tags
, not a good plan as noted in step
14, but let's just assume no tags). So now we get another
disjoint subgraph withorigin/Q/*
names. For simplicity
let's just assume that onlyorigin/Q/master
exists this time:A--B <-- master
|
| -------C--D <-- P/master
/
P1-P2-...-Pm
Pd
---E--F <-- P/dev
Q1-Q2-...-Qm <-- origin/Q/masterRun
git checkout -b Q/master master
:A--B <-- master, Q/master
|
| -------C--D <-- P/master
/
P1-P2-...-Pm
Pd
---E--F <-- P/dev
Q1-Q2-...-Qm <-- origin/Q/masterRun the (probably ineffectual and still mysterious)
git reset --hard
andgit clean
steps.Use the funky two step merge with
--allow-unrelated-histories
to create new commitG
like this:---------------G <-- Q/master
/ |
A--B <-- master | (down to Qm)
|
| -------C--D <-- P/master
/
P1-P2-...-Pm
Pd
---E--F <-- P/dev
/ (up to G)
/
Q1-Q2-...-Qm <-- origin/Q/masterAgain, optional: rename all files in G to live in Q/ and
commit. Again let's assume this does happen:---------------G--H <-- Q/master
/ |
A--B <-- master | (down to Qm)
|
| -------C--D <-- P/master
/
P1-P2-...-Pm
Pd
---E--F <-- P/dev
/ (up to G)
/
Q1-Q2-...-Qm <-- origin/Q/masterUgly attempt to rename tags; we'll ignore this.
Remove remote
Q
andorigin/Q/*
names. (No need to draw this.)Repeat outer loop for repository R. Assuming it has only
its ownmaster
, we'll get a tangled graph like this:--------------------I--J <-- R/master
/ | (down to Rm)
/
| ---------------G--H <-- Q/master
|/ |
A--B <-- master | (down to Qm)
|
| -------C--D <-- P/master
/
P1-P2-...-Pm
Pd
---E--F <-- P/dev
/ (up to G)
/
Q1-Q2-...-Qm
/ (up to I)
/
R1-R2-...----Rm
(end of analysis)
add a comment |
Your Answer
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55285121%2fgit-merge-after-renaming-of-all-files%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
This really isn't a very good answer as it's more about the script you used—or perhaps I should say, the script you didn't use, as your comment says that you used one based on the script to which you linked—but I'll show the rather tangled graph I get in a hypothetical script-conversion of some original repositories below. Note that this particular script leaves all the conversions with a merge base commit of, in essence, commit B
, and commit B
itself is empty.
Your question says:
now I want to merge branch
source
into branchtarget
, noting that I made sure the two were in sync right before the final cutover.
As you'll see below, all the new branches are named after the project that they came from—there's no clear way to map source
and target
onto, e.g., P or Q. But if you were to run:
git checkout P/master
git merge Q/master
after the process illustrated below, the merge base for this git merge
would be empty-commit-B
and the merge would go smoothly: Git would look at the commits I drew as D
and H
respectively, trace their ancestries, find commit B
as their merge base, and run two git diff
s:
git diff <hash-of-B> <hash-of-D> # what we did on P/master
git diff <hash-of-B> <hash-of-H> # what they did on H/master
The output of these git diff
s would say that every file is created from scratch, and all their names are different: everything in P/master
is named P/*
and everything in H/master
is named Q/*
. There would be no name collisions and the merge would complete on its own.
Clearly, that's not what you're doing, then. But what you are doing, and which commit is the merge base, remains mysterious. It looks like you're picking out two tip commits such that the merge base of the two tip commits is a commit that does have files, and those files are not yet renamed from base to tip.
The point of the script you linked is to set things up so that the merge bases of each of the unrelated projects is an empty commit. Probably, the thing to do after that script—or in place of that script, really—is to do one massive octopus merge of all the final commits (NB: this is untested, as is probably obvious):
git checkout P/master # just to be somewhere that's not master
git branch -d master # discard existing master branch name
git checkout --orphan master # arrange to create new master
git merge P/master Q/master R/master # make big octopus merge to marry all projects
The merge base of this octopus merge would again be commit B
, and the result would be one merge that brings all the projects in under their new project/*
names. The original repositories are now all mostly useless, though if there are new commits in them, you can fetch from them, add a renaming commit, and merge from the renaming commit (this would be easier if the importing script didn't delete the added remotes).
Observations on the workings of the linked script
I've never faced this particular problem, but the approach in the script seems like a not-unreasonable starting point. I'd probably do it a bit differently, not bothering with an empty merge base and using git read-tree
and git commit-tree
to build and create the end octopus merge. The main key is to add a rename commit at the end of each incoming project branch (P/*
, Q/*
, etc) in the sketch below.
The script seems to work this way. It has as inputs projects P, Q, R (URLs whose last component is treated a project name).
- Make empty repo.
Make two iniial commits:
A--B <-- master
Commit A has one file, commit B has no files (why not just
commit the empty tree as B? but never mind).
Loop, for all three projects. Here I have expanded the loop to view
what's happening.(loop iteration 1)
git remote add P <url>
andgit fetch P
(with--tags
!?). We're going to assume here that P has master and dev.A--B <-- master
P1-P2-...-Pm <-- origin/P/master
Pd <-- origin/P/devUse git ls-remote --heads to find names for commits in P, i.e.,
the same set of names we have inrefs/remotes/P/*
. (Assumes the
remote hsa not changed during fetch -- unwise but probably OK.)Loop over these names. Result again expanded in line for illustration...
Run
git checkout -b P/master master
. Effect:A--B <-- master, P/master
P1-P2-...-Pm <-- origin/P/master
Pd <-- origin/P/devRun
git reset --hard
for no apparent reason: no effect. Perhaps
this might have some effect on some later step.Run
git clean -d --force
for no apparent reason: no effect.Run
git merge --allow-unrelated-histories --no-commit remotes/P/master"
git commit -m ...`.
(does merge, but does not commit yet) and then run
Effect:A--B <-- master
-------C <-- P/master
/
P1-P2-...-Pm <-- origin/P/master
Pd <-- origin/P/devMaybe rename files, with somewhat squirrelly code (lines 160-180):
if project P has one top level directory named P, do nothing, otherwise
create directory named P (with no check to see if this fails) and
then in effect:git mv all-but-P P/
git commit -m "[Project] Move $sub_project files into sub directory"giving:
A--B <-- master
-------C--D <-- P/master
/
P1-P2-...-Pm <-- origin/P/master
Pd <-- origin/P/devNote that the
git mv
is given-k
so that it does nothing if
one of thegit mv
operations would have failed. However, except
for subdirectory P and.git
itself, all files in the top level
of the work-tree should be in the index and thegit mv
should
succeed unless one of them is namedP
(in which case, yikes!).I assume here that we did the mv, otherwise commit
D
does not exist.Repeat loop (see step 5) for
dev
. Rungit checkout -b P/dev master
:A--B <-- master, P/dev
-------C--D <-- P/master
/
P1-P2-...-Pm <-- origin/P/master
Pd <-- origin/P/devPresumably-ineffectual
git reset
andgit clean
again
as in steps 7 and 8. (This might do something if thegit mv
in step 10 went really badly?) Do a funky two step merge as in
step 9, giving:A--B <-- master
|
| -------C--D <-- P/master
/
P1-P2-...-Pm <-- origin/P/master
Pd <-- origin/P/dev
---E <-- P/devwhere the line down from B connects to the one up from E. The
graph has gotten rather out of hand at this point.Rename and commit as in step 10. I assume here that the
project isn't already in a subdirectory, in bothmaster
, as
already assumed, anddev
.A--B <-- master
|
| -------C--D <-- P/master
/
P1-P2-...-Pm <-- origin/P/master
Pd <-- origin/P/dev
---E--F <-- P/devReally ugly attempt to rename tags, at lines 190-207. This
should have been done at fetch time, using a clever refspec.
Whoever wrote this probably was not aware of annotated vs
lightweight tags. It is not clear to me whether this works
correctly and I did not look closely. Let's just assume no tags
for now.Remove remote P. This removes the
origin/P/*
names too,
but of course the commits stick around as they're retained by
the newP/*
branches:A--B <-- master
|
| -------C--D <-- P/master
/
P1-P2-...-Pm
Pd
---E--F <-- P/devRepeat outer loop (step 3) with remote Q. We'll add Q and
fetch (again with--tags
, not a good plan as noted in step
14, but let's just assume no tags). So now we get another
disjoint subgraph withorigin/Q/*
names. For simplicity
let's just assume that onlyorigin/Q/master
exists this time:A--B <-- master
|
| -------C--D <-- P/master
/
P1-P2-...-Pm
Pd
---E--F <-- P/dev
Q1-Q2-...-Qm <-- origin/Q/masterRun
git checkout -b Q/master master
:A--B <-- master, Q/master
|
| -------C--D <-- P/master
/
P1-P2-...-Pm
Pd
---E--F <-- P/dev
Q1-Q2-...-Qm <-- origin/Q/masterRun the (probably ineffectual and still mysterious)
git reset --hard
andgit clean
steps.Use the funky two step merge with
--allow-unrelated-histories
to create new commitG
like this:---------------G <-- Q/master
/ |
A--B <-- master | (down to Qm)
|
| -------C--D <-- P/master
/
P1-P2-...-Pm
Pd
---E--F <-- P/dev
/ (up to G)
/
Q1-Q2-...-Qm <-- origin/Q/masterAgain, optional: rename all files in G to live in Q/ and
commit. Again let's assume this does happen:---------------G--H <-- Q/master
/ |
A--B <-- master | (down to Qm)
|
| -------C--D <-- P/master
/
P1-P2-...-Pm
Pd
---E--F <-- P/dev
/ (up to G)
/
Q1-Q2-...-Qm <-- origin/Q/masterUgly attempt to rename tags; we'll ignore this.
Remove remote
Q
andorigin/Q/*
names. (No need to draw this.)Repeat outer loop for repository R. Assuming it has only
its ownmaster
, we'll get a tangled graph like this:--------------------I--J <-- R/master
/ | (down to Rm)
/
| ---------------G--H <-- Q/master
|/ |
A--B <-- master | (down to Qm)
|
| -------C--D <-- P/master
/
P1-P2-...-Pm
Pd
---E--F <-- P/dev
/ (up to G)
/
Q1-Q2-...-Qm
/ (up to I)
/
R1-R2-...----Rm
(end of analysis)
add a comment |
This really isn't a very good answer as it's more about the script you used—or perhaps I should say, the script you didn't use, as your comment says that you used one based on the script to which you linked—but I'll show the rather tangled graph I get in a hypothetical script-conversion of some original repositories below. Note that this particular script leaves all the conversions with a merge base commit of, in essence, commit B
, and commit B
itself is empty.
Your question says:
now I want to merge branch
source
into branchtarget
, noting that I made sure the two were in sync right before the final cutover.
As you'll see below, all the new branches are named after the project that they came from—there's no clear way to map source
and target
onto, e.g., P or Q. But if you were to run:
git checkout P/master
git merge Q/master
after the process illustrated below, the merge base for this git merge
would be empty-commit-B
and the merge would go smoothly: Git would look at the commits I drew as D
and H
respectively, trace their ancestries, find commit B
as their merge base, and run two git diff
s:
git diff <hash-of-B> <hash-of-D> # what we did on P/master
git diff <hash-of-B> <hash-of-H> # what they did on H/master
The output of these git diff
s would say that every file is created from scratch, and all their names are different: everything in P/master
is named P/*
and everything in H/master
is named Q/*
. There would be no name collisions and the merge would complete on its own.
Clearly, that's not what you're doing, then. But what you are doing, and which commit is the merge base, remains mysterious. It looks like you're picking out two tip commits such that the merge base of the two tip commits is a commit that does have files, and those files are not yet renamed from base to tip.
The point of the script you linked is to set things up so that the merge bases of each of the unrelated projects is an empty commit. Probably, the thing to do after that script—or in place of that script, really—is to do one massive octopus merge of all the final commits (NB: this is untested, as is probably obvious):
git checkout P/master # just to be somewhere that's not master
git branch -d master # discard existing master branch name
git checkout --orphan master # arrange to create new master
git merge P/master Q/master R/master # make big octopus merge to marry all projects
The merge base of this octopus merge would again be commit B
, and the result would be one merge that brings all the projects in under their new project/*
names. The original repositories are now all mostly useless, though if there are new commits in them, you can fetch from them, add a renaming commit, and merge from the renaming commit (this would be easier if the importing script didn't delete the added remotes).
Observations on the workings of the linked script
I've never faced this particular problem, but the approach in the script seems like a not-unreasonable starting point. I'd probably do it a bit differently, not bothering with an empty merge base and using git read-tree
and git commit-tree
to build and create the end octopus merge. The main key is to add a rename commit at the end of each incoming project branch (P/*
, Q/*
, etc) in the sketch below.
The script seems to work this way. It has as inputs projects P, Q, R (URLs whose last component is treated a project name).
- Make empty repo.
Make two iniial commits:
A--B <-- master
Commit A has one file, commit B has no files (why not just
commit the empty tree as B? but never mind).
Loop, for all three projects. Here I have expanded the loop to view
what's happening.(loop iteration 1)
git remote add P <url>
andgit fetch P
(with--tags
!?). We're going to assume here that P has master and dev.A--B <-- master
P1-P2-...-Pm <-- origin/P/master
Pd <-- origin/P/devUse git ls-remote --heads to find names for commits in P, i.e.,
the same set of names we have inrefs/remotes/P/*
. (Assumes the
remote hsa not changed during fetch -- unwise but probably OK.)Loop over these names. Result again expanded in line for illustration...
Run
git checkout -b P/master master
. Effect:A--B <-- master, P/master
P1-P2-...-Pm <-- origin/P/master
Pd <-- origin/P/devRun
git reset --hard
for no apparent reason: no effect. Perhaps
this might have some effect on some later step.Run
git clean -d --force
for no apparent reason: no effect.Run
git merge --allow-unrelated-histories --no-commit remotes/P/master"
git commit -m ...`.
(does merge, but does not commit yet) and then run
Effect:A--B <-- master
-------C <-- P/master
/
P1-P2-...-Pm <-- origin/P/master
Pd <-- origin/P/devMaybe rename files, with somewhat squirrelly code (lines 160-180):
if project P has one top level directory named P, do nothing, otherwise
create directory named P (with no check to see if this fails) and
then in effect:git mv all-but-P P/
git commit -m "[Project] Move $sub_project files into sub directory"giving:
A--B <-- master
-------C--D <-- P/master
/
P1-P2-...-Pm <-- origin/P/master
Pd <-- origin/P/devNote that the
git mv
is given-k
so that it does nothing if
one of thegit mv
operations would have failed. However, except
for subdirectory P and.git
itself, all files in the top level
of the work-tree should be in the index and thegit mv
should
succeed unless one of them is namedP
(in which case, yikes!).I assume here that we did the mv, otherwise commit
D
does not exist.Repeat loop (see step 5) for
dev
. Rungit checkout -b P/dev master
:A--B <-- master, P/dev
-------C--D <-- P/master
/
P1-P2-...-Pm <-- origin/P/master
Pd <-- origin/P/devPresumably-ineffectual
git reset
andgit clean
again
as in steps 7 and 8. (This might do something if thegit mv
in step 10 went really badly?) Do a funky two step merge as in
step 9, giving:A--B <-- master
|
| -------C--D <-- P/master
/
P1-P2-...-Pm <-- origin/P/master
Pd <-- origin/P/dev
---E <-- P/devwhere the line down from B connects to the one up from E. The
graph has gotten rather out of hand at this point.Rename and commit as in step 10. I assume here that the
project isn't already in a subdirectory, in bothmaster
, as
already assumed, anddev
.A--B <-- master
|
| -------C--D <-- P/master
/
P1-P2-...-Pm <-- origin/P/master
Pd <-- origin/P/dev
---E--F <-- P/devReally ugly attempt to rename tags, at lines 190-207. This
should have been done at fetch time, using a clever refspec.
Whoever wrote this probably was not aware of annotated vs
lightweight tags. It is not clear to me whether this works
correctly and I did not look closely. Let's just assume no tags
for now.Remove remote P. This removes the
origin/P/*
names too,
but of course the commits stick around as they're retained by
the newP/*
branches:A--B <-- master
|
| -------C--D <-- P/master
/
P1-P2-...-Pm
Pd
---E--F <-- P/devRepeat outer loop (step 3) with remote Q. We'll add Q and
fetch (again with--tags
, not a good plan as noted in step
14, but let's just assume no tags). So now we get another
disjoint subgraph withorigin/Q/*
names. For simplicity
let's just assume that onlyorigin/Q/master
exists this time:A--B <-- master
|
| -------C--D <-- P/master
/
P1-P2-...-Pm
Pd
---E--F <-- P/dev
Q1-Q2-...-Qm <-- origin/Q/masterRun
git checkout -b Q/master master
:A--B <-- master, Q/master
|
| -------C--D <-- P/master
/
P1-P2-...-Pm
Pd
---E--F <-- P/dev
Q1-Q2-...-Qm <-- origin/Q/masterRun the (probably ineffectual and still mysterious)
git reset --hard
andgit clean
steps.Use the funky two step merge with
--allow-unrelated-histories
to create new commitG
like this:---------------G <-- Q/master
/ |
A--B <-- master | (down to Qm)
|
| -------C--D <-- P/master
/
P1-P2-...-Pm
Pd
---E--F <-- P/dev
/ (up to G)
/
Q1-Q2-...-Qm <-- origin/Q/masterAgain, optional: rename all files in G to live in Q/ and
commit. Again let's assume this does happen:---------------G--H <-- Q/master
/ |
A--B <-- master | (down to Qm)
|
| -------C--D <-- P/master
/
P1-P2-...-Pm
Pd
---E--F <-- P/dev
/ (up to G)
/
Q1-Q2-...-Qm <-- origin/Q/masterUgly attempt to rename tags; we'll ignore this.
Remove remote
Q
andorigin/Q/*
names. (No need to draw this.)Repeat outer loop for repository R. Assuming it has only
its ownmaster
, we'll get a tangled graph like this:--------------------I--J <-- R/master
/ | (down to Rm)
/
| ---------------G--H <-- Q/master
|/ |
A--B <-- master | (down to Qm)
|
| -------C--D <-- P/master
/
P1-P2-...-Pm
Pd
---E--F <-- P/dev
/ (up to G)
/
Q1-Q2-...-Qm
/ (up to I)
/
R1-R2-...----Rm
(end of analysis)
add a comment |
This really isn't a very good answer as it's more about the script you used—or perhaps I should say, the script you didn't use, as your comment says that you used one based on the script to which you linked—but I'll show the rather tangled graph I get in a hypothetical script-conversion of some original repositories below. Note that this particular script leaves all the conversions with a merge base commit of, in essence, commit B
, and commit B
itself is empty.
Your question says:
now I want to merge branch
source
into branchtarget
, noting that I made sure the two were in sync right before the final cutover.
As you'll see below, all the new branches are named after the project that they came from—there's no clear way to map source
and target
onto, e.g., P or Q. But if you were to run:
git checkout P/master
git merge Q/master
after the process illustrated below, the merge base for this git merge
would be empty-commit-B
and the merge would go smoothly: Git would look at the commits I drew as D
and H
respectively, trace their ancestries, find commit B
as their merge base, and run two git diff
s:
git diff <hash-of-B> <hash-of-D> # what we did on P/master
git diff <hash-of-B> <hash-of-H> # what they did on H/master
The output of these git diff
s would say that every file is created from scratch, and all their names are different: everything in P/master
is named P/*
and everything in H/master
is named Q/*
. There would be no name collisions and the merge would complete on its own.
Clearly, that's not what you're doing, then. But what you are doing, and which commit is the merge base, remains mysterious. It looks like you're picking out two tip commits such that the merge base of the two tip commits is a commit that does have files, and those files are not yet renamed from base to tip.
The point of the script you linked is to set things up so that the merge bases of each of the unrelated projects is an empty commit. Probably, the thing to do after that script—or in place of that script, really—is to do one massive octopus merge of all the final commits (NB: this is untested, as is probably obvious):
git checkout P/master # just to be somewhere that's not master
git branch -d master # discard existing master branch name
git checkout --orphan master # arrange to create new master
git merge P/master Q/master R/master # make big octopus merge to marry all projects
The merge base of this octopus merge would again be commit B
, and the result would be one merge that brings all the projects in under their new project/*
names. The original repositories are now all mostly useless, though if there are new commits in them, you can fetch from them, add a renaming commit, and merge from the renaming commit (this would be easier if the importing script didn't delete the added remotes).
Observations on the workings of the linked script
I've never faced this particular problem, but the approach in the script seems like a not-unreasonable starting point. I'd probably do it a bit differently, not bothering with an empty merge base and using git read-tree
and git commit-tree
to build and create the end octopus merge. The main key is to add a rename commit at the end of each incoming project branch (P/*
, Q/*
, etc) in the sketch below.
The script seems to work this way. It has as inputs projects P, Q, R (URLs whose last component is treated a project name).
- Make empty repo.
Make two iniial commits:
A--B <-- master
Commit A has one file, commit B has no files (why not just
commit the empty tree as B? but never mind).
Loop, for all three projects. Here I have expanded the loop to view
what's happening.(loop iteration 1)
git remote add P <url>
andgit fetch P
(with--tags
!?). We're going to assume here that P has master and dev.A--B <-- master
P1-P2-...-Pm <-- origin/P/master
Pd <-- origin/P/devUse git ls-remote --heads to find names for commits in P, i.e.,
the same set of names we have inrefs/remotes/P/*
. (Assumes the
remote hsa not changed during fetch -- unwise but probably OK.)Loop over these names. Result again expanded in line for illustration...
Run
git checkout -b P/master master
. Effect:A--B <-- master, P/master
P1-P2-...-Pm <-- origin/P/master
Pd <-- origin/P/devRun
git reset --hard
for no apparent reason: no effect. Perhaps
this might have some effect on some later step.Run
git clean -d --force
for no apparent reason: no effect.Run
git merge --allow-unrelated-histories --no-commit remotes/P/master"
git commit -m ...`.
(does merge, but does not commit yet) and then run
Effect:A--B <-- master
-------C <-- P/master
/
P1-P2-...-Pm <-- origin/P/master
Pd <-- origin/P/devMaybe rename files, with somewhat squirrelly code (lines 160-180):
if project P has one top level directory named P, do nothing, otherwise
create directory named P (with no check to see if this fails) and
then in effect:git mv all-but-P P/
git commit -m "[Project] Move $sub_project files into sub directory"giving:
A--B <-- master
-------C--D <-- P/master
/
P1-P2-...-Pm <-- origin/P/master
Pd <-- origin/P/devNote that the
git mv
is given-k
so that it does nothing if
one of thegit mv
operations would have failed. However, except
for subdirectory P and.git
itself, all files in the top level
of the work-tree should be in the index and thegit mv
should
succeed unless one of them is namedP
(in which case, yikes!).I assume here that we did the mv, otherwise commit
D
does not exist.Repeat loop (see step 5) for
dev
. Rungit checkout -b P/dev master
:A--B <-- master, P/dev
-------C--D <-- P/master
/
P1-P2-...-Pm <-- origin/P/master
Pd <-- origin/P/devPresumably-ineffectual
git reset
andgit clean
again
as in steps 7 and 8. (This might do something if thegit mv
in step 10 went really badly?) Do a funky two step merge as in
step 9, giving:A--B <-- master
|
| -------C--D <-- P/master
/
P1-P2-...-Pm <-- origin/P/master
Pd <-- origin/P/dev
---E <-- P/devwhere the line down from B connects to the one up from E. The
graph has gotten rather out of hand at this point.Rename and commit as in step 10. I assume here that the
project isn't already in a subdirectory, in bothmaster
, as
already assumed, anddev
.A--B <-- master
|
| -------C--D <-- P/master
/
P1-P2-...-Pm <-- origin/P/master
Pd <-- origin/P/dev
---E--F <-- P/devReally ugly attempt to rename tags, at lines 190-207. This
should have been done at fetch time, using a clever refspec.
Whoever wrote this probably was not aware of annotated vs
lightweight tags. It is not clear to me whether this works
correctly and I did not look closely. Let's just assume no tags
for now.Remove remote P. This removes the
origin/P/*
names too,
but of course the commits stick around as they're retained by
the newP/*
branches:A--B <-- master
|
| -------C--D <-- P/master
/
P1-P2-...-Pm
Pd
---E--F <-- P/devRepeat outer loop (step 3) with remote Q. We'll add Q and
fetch (again with--tags
, not a good plan as noted in step
14, but let's just assume no tags). So now we get another
disjoint subgraph withorigin/Q/*
names. For simplicity
let's just assume that onlyorigin/Q/master
exists this time:A--B <-- master
|
| -------C--D <-- P/master
/
P1-P2-...-Pm
Pd
---E--F <-- P/dev
Q1-Q2-...-Qm <-- origin/Q/masterRun
git checkout -b Q/master master
:A--B <-- master, Q/master
|
| -------C--D <-- P/master
/
P1-P2-...-Pm
Pd
---E--F <-- P/dev
Q1-Q2-...-Qm <-- origin/Q/masterRun the (probably ineffectual and still mysterious)
git reset --hard
andgit clean
steps.Use the funky two step merge with
--allow-unrelated-histories
to create new commitG
like this:---------------G <-- Q/master
/ |
A--B <-- master | (down to Qm)
|
| -------C--D <-- P/master
/
P1-P2-...-Pm
Pd
---E--F <-- P/dev
/ (up to G)
/
Q1-Q2-...-Qm <-- origin/Q/masterAgain, optional: rename all files in G to live in Q/ and
commit. Again let's assume this does happen:---------------G--H <-- Q/master
/ |
A--B <-- master | (down to Qm)
|
| -------C--D <-- P/master
/
P1-P2-...-Pm
Pd
---E--F <-- P/dev
/ (up to G)
/
Q1-Q2-...-Qm <-- origin/Q/masterUgly attempt to rename tags; we'll ignore this.
Remove remote
Q
andorigin/Q/*
names. (No need to draw this.)Repeat outer loop for repository R. Assuming it has only
its ownmaster
, we'll get a tangled graph like this:--------------------I--J <-- R/master
/ | (down to Rm)
/
| ---------------G--H <-- Q/master
|/ |
A--B <-- master | (down to Qm)
|
| -------C--D <-- P/master
/
P1-P2-...-Pm
Pd
---E--F <-- P/dev
/ (up to G)
/
Q1-Q2-...-Qm
/ (up to I)
/
R1-R2-...----Rm
(end of analysis)
This really isn't a very good answer as it's more about the script you used—or perhaps I should say, the script you didn't use, as your comment says that you used one based on the script to which you linked—but I'll show the rather tangled graph I get in a hypothetical script-conversion of some original repositories below. Note that this particular script leaves all the conversions with a merge base commit of, in essence, commit B
, and commit B
itself is empty.
Your question says:
now I want to merge branch
source
into branchtarget
, noting that I made sure the two were in sync right before the final cutover.
As you'll see below, all the new branches are named after the project that they came from—there's no clear way to map source
and target
onto, e.g., P or Q. But if you were to run:
git checkout P/master
git merge Q/master
after the process illustrated below, the merge base for this git merge
would be empty-commit-B
and the merge would go smoothly: Git would look at the commits I drew as D
and H
respectively, trace their ancestries, find commit B
as their merge base, and run two git diff
s:
git diff <hash-of-B> <hash-of-D> # what we did on P/master
git diff <hash-of-B> <hash-of-H> # what they did on H/master
The output of these git diff
s would say that every file is created from scratch, and all their names are different: everything in P/master
is named P/*
and everything in H/master
is named Q/*
. There would be no name collisions and the merge would complete on its own.
Clearly, that's not what you're doing, then. But what you are doing, and which commit is the merge base, remains mysterious. It looks like you're picking out two tip commits such that the merge base of the two tip commits is a commit that does have files, and those files are not yet renamed from base to tip.
The point of the script you linked is to set things up so that the merge bases of each of the unrelated projects is an empty commit. Probably, the thing to do after that script—or in place of that script, really—is to do one massive octopus merge of all the final commits (NB: this is untested, as is probably obvious):
git checkout P/master # just to be somewhere that's not master
git branch -d master # discard existing master branch name
git checkout --orphan master # arrange to create new master
git merge P/master Q/master R/master # make big octopus merge to marry all projects
The merge base of this octopus merge would again be commit B
, and the result would be one merge that brings all the projects in under their new project/*
names. The original repositories are now all mostly useless, though if there are new commits in them, you can fetch from them, add a renaming commit, and merge from the renaming commit (this would be easier if the importing script didn't delete the added remotes).
Observations on the workings of the linked script
I've never faced this particular problem, but the approach in the script seems like a not-unreasonable starting point. I'd probably do it a bit differently, not bothering with an empty merge base and using git read-tree
and git commit-tree
to build and create the end octopus merge. The main key is to add a rename commit at the end of each incoming project branch (P/*
, Q/*
, etc) in the sketch below.
The script seems to work this way. It has as inputs projects P, Q, R (URLs whose last component is treated a project name).
- Make empty repo.
Make two iniial commits:
A--B <-- master
Commit A has one file, commit B has no files (why not just
commit the empty tree as B? but never mind).
Loop, for all three projects. Here I have expanded the loop to view
what's happening.(loop iteration 1)
git remote add P <url>
andgit fetch P
(with--tags
!?). We're going to assume here that P has master and dev.A--B <-- master
P1-P2-...-Pm <-- origin/P/master
Pd <-- origin/P/devUse git ls-remote --heads to find names for commits in P, i.e.,
the same set of names we have inrefs/remotes/P/*
. (Assumes the
remote hsa not changed during fetch -- unwise but probably OK.)Loop over these names. Result again expanded in line for illustration...
Run
git checkout -b P/master master
. Effect:A--B <-- master, P/master
P1-P2-...-Pm <-- origin/P/master
Pd <-- origin/P/devRun
git reset --hard
for no apparent reason: no effect. Perhaps
this might have some effect on some later step.Run
git clean -d --force
for no apparent reason: no effect.Run
git merge --allow-unrelated-histories --no-commit remotes/P/master"
git commit -m ...`.
(does merge, but does not commit yet) and then run
Effect:A--B <-- master
-------C <-- P/master
/
P1-P2-...-Pm <-- origin/P/master
Pd <-- origin/P/devMaybe rename files, with somewhat squirrelly code (lines 160-180):
if project P has one top level directory named P, do nothing, otherwise
create directory named P (with no check to see if this fails) and
then in effect:git mv all-but-P P/
git commit -m "[Project] Move $sub_project files into sub directory"giving:
A--B <-- master
-------C--D <-- P/master
/
P1-P2-...-Pm <-- origin/P/master
Pd <-- origin/P/devNote that the
git mv
is given-k
so that it does nothing if
one of thegit mv
operations would have failed. However, except
for subdirectory P and.git
itself, all files in the top level
of the work-tree should be in the index and thegit mv
should
succeed unless one of them is namedP
(in which case, yikes!).I assume here that we did the mv, otherwise commit
D
does not exist.Repeat loop (see step 5) for
dev
. Rungit checkout -b P/dev master
:A--B <-- master, P/dev
-------C--D <-- P/master
/
P1-P2-...-Pm <-- origin/P/master
Pd <-- origin/P/devPresumably-ineffectual
git reset
andgit clean
again
as in steps 7 and 8. (This might do something if thegit mv
in step 10 went really badly?) Do a funky two step merge as in
step 9, giving:A--B <-- master
|
| -------C--D <-- P/master
/
P1-P2-...-Pm <-- origin/P/master
Pd <-- origin/P/dev
---E <-- P/devwhere the line down from B connects to the one up from E. The
graph has gotten rather out of hand at this point.Rename and commit as in step 10. I assume here that the
project isn't already in a subdirectory, in bothmaster
, as
already assumed, anddev
.A--B <-- master
|
| -------C--D <-- P/master
/
P1-P2-...-Pm <-- origin/P/master
Pd <-- origin/P/dev
---E--F <-- P/devReally ugly attempt to rename tags, at lines 190-207. This
should have been done at fetch time, using a clever refspec.
Whoever wrote this probably was not aware of annotated vs
lightweight tags. It is not clear to me whether this works
correctly and I did not look closely. Let's just assume no tags
for now.Remove remote P. This removes the
origin/P/*
names too,
but of course the commits stick around as they're retained by
the newP/*
branches:A--B <-- master
|
| -------C--D <-- P/master
/
P1-P2-...-Pm
Pd
---E--F <-- P/devRepeat outer loop (step 3) with remote Q. We'll add Q and
fetch (again with--tags
, not a good plan as noted in step
14, but let's just assume no tags). So now we get another
disjoint subgraph withorigin/Q/*
names. For simplicity
let's just assume that onlyorigin/Q/master
exists this time:A--B <-- master
|
| -------C--D <-- P/master
/
P1-P2-...-Pm
Pd
---E--F <-- P/dev
Q1-Q2-...-Qm <-- origin/Q/masterRun
git checkout -b Q/master master
:A--B <-- master, Q/master
|
| -------C--D <-- P/master
/
P1-P2-...-Pm
Pd
---E--F <-- P/dev
Q1-Q2-...-Qm <-- origin/Q/masterRun the (probably ineffectual and still mysterious)
git reset --hard
andgit clean
steps.Use the funky two step merge with
--allow-unrelated-histories
to create new commitG
like this:---------------G <-- Q/master
/ |
A--B <-- master | (down to Qm)
|
| -------C--D <-- P/master
/
P1-P2-...-Pm
Pd
---E--F <-- P/dev
/ (up to G)
/
Q1-Q2-...-Qm <-- origin/Q/masterAgain, optional: rename all files in G to live in Q/ and
commit. Again let's assume this does happen:---------------G--H <-- Q/master
/ |
A--B <-- master | (down to Qm)
|
| -------C--D <-- P/master
/
P1-P2-...-Pm
Pd
---E--F <-- P/dev
/ (up to G)
/
Q1-Q2-...-Qm <-- origin/Q/masterUgly attempt to rename tags; we'll ignore this.
Remove remote
Q
andorigin/Q/*
names. (No need to draw this.)Repeat outer loop for repository R. Assuming it has only
its ownmaster
, we'll get a tangled graph like this:--------------------I--J <-- R/master
/ | (down to Rm)
/
| ---------------G--H <-- Q/master
|/ |
A--B <-- master | (down to Qm)
|
| -------C--D <-- P/master
/
P1-P2-...-Pm
Pd
---E--F <-- P/dev
/ (up to G)
/
Q1-Q2-...-Qm
/ (up to I)
/
R1-R2-...----Rm
(end of analysis)
answered Mar 23 at 18:21
torektorek
198k18246328
198k18246328
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55285121%2fgit-merge-after-renaming-of-all-files%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
I note that you used
--no-commit
in the second example. If you did that in the first (git merge -s ours
), that would explain the problem. If not, it might help to provide a lot more detail on what you did to get into this situation (the first link you posted talks about using subtree merging so more detail could be important).– torek
Mar 21 at 18:13
My question: are the new branches you're going to want to commit in the future, branches that were created before or after the initial mega-merge? In the situation you describe, I would expect such future merges to be fast, only merges from a branch that predates the mega merge to be slow. Does your workflow allow giving up on pre-mega-merge branches, or at least progressively phasing them out?
– joanis
Mar 21 at 19:52
Second question, geared towards answering yours more directly: does
git merge-base master target
point to a commit before or after the mega-merge? Third question: are the file names insource
andtarget
the same, or pre & post renaming?– joanis
Mar 21 at 19:55
@torek The use of
--no-commit
was arbitrary and was not used in the initialgit merge -s ours
merge.– tanager
Mar 22 at 12:16
@joanis Both
source
andtarget
were created before the mega-merge. Yes, we will eventually give up onsource
, but not for a few months at least.– tanager
Mar 22 at 12:29