I recently switched to a new computer, where I copied all files from my old. Long story short, I did some mistakes setting up git and GitHub remote repositories correctly. I think it started with being unable to reconnect to GitHub, and I ended up deleting my old .git
folder (probably not the wisest move).
I have two branches master and apprentice, where I use the apprentice branch for trying out stuff, and then merging into master when I get the code working.
On my new computer, I started working locally and committed to the local apprentice branch. I then tried to push to remote branch, which didn't work. I then force pushed, and ended up overwriting my commit history on apprentice, effectively losing all commits on this branch prior to switching computer. This sucked, but I didn't see a way to undo the damage, neither was it terribly important as I had my most recent code. I, therefore kept working and making commits on apprentice.
My current problem stems from trying to merge the changes on apprentice into master.
Since the entire commit history on apprentice was effectively rewritten, a simple merge didn't work, so I therefore tried merging using the --allow-unrelated-histories
flag. This allowed the merge to proceed, after manually resolving all merge conflicts. I was then able to finalise the commit and push to remote, and the remote master branch now had the added changes from apprentice. (Resolving merge conflicts, committing and pushing was done in VS Code.)
% git checkout master
% git merge apprentice
fatal: refusing to merge unrelated histories
% git merge --squash --allow-unrelated-histories apprentice
Auto-merging .Rprofile
CONFLICT (add/add): Merge conflict in <myfile_1>
CONFLICT (add/add): Merge conflict in <myfile_2>
⋮
CONFLICT (add/add): Merge conflict in <myfile_n>
Automatic merge failed; fix conflicts and then commit the result.
Subsequent to merging and pushing, I updated all remote repos
git push --all
However, GitHub tells me apprentice "is [still] 6 commits ahead, 19 commits behind master".*
Looking into what those differences are, it only tells me that there's nothing to compare because apprentice and master are entirely different commit histories.
I therefore tried to merge again (this time no --squash
):
% git merge --no-ff -X theirs master
fatal: refusing to merge unrelated histories
% git merge --allow-unrelated-histories apprentice
Auto-merging .Rprofile
CONFLICT (add/add): Merge conflict in <myfile_1>
CONFLICT (add/add): Merge conflict in <myfile_2>
⋮
CONFLICT (add/add): Merge conflict in <myfile_n>
Automatic merge failed; fix conflicts and then commit the result.
Like before, I proceeded to resolve the conflicts manually in VS Code, committed changes and pushed to remote. This resulted in the commits on apprentice being added on top of the previous squashed commit. However, GitHub still tells me that master and apprentice are entirely different commit histories.
This leads me to the following questions:
--allow-unrelated-histories
flag) in subsequent merges of the two branches?Basically, you had this situation, due to restarting the branch on a new computer:
master
v
O--O--O--O--O--O
O--O--O--O--O
^
apprentice
If you had done a normal merge, with --allow-unrelated-histories, you would've had this situation:
master
v
O--O--O--O--O--O--M
/
/
O--O--O--O--O
^
apprentice
This would've been fine, and any further work on apprentice
could be merged on top of master
and git would handle this as you expect, only trying to merge the changes introduced by the new commits.
However, since you did a squash, you have this situation instead:
master
v
O--O--O--O--O--O--S
O--O--O--O--O
^
apprentice
S
here represents one or more squash commits that were produced by squashing the commits from apprentice
. Notice one thing, there's no merge commit involved. As such, git does not store any knowledge of the relationship between apprentice
and master
, and the next time you try to do another merge between the two, all the commits will again be in play, replaying all those merge conflicts you resolved during the squash+merge.
If you intend to squash the commits when merging, my advice would be to adopt one of the following two methods:
Squash the commits or otherwise clean up the history on apprentice
before you merge, this way both branches have the squashed commits, and a proper merge commit can be created
master
v
O--O--O--O--O--O--M
/
/
S
^
apprentice
After doing the squash+merge, drop and recreate your apprentice
branch on top of master
master
v
O--O--O--O--O--O--S
^
apprentice
O--O--O--O--O
^
(no reference, will be garbage collected)
The third option I did not present, which you might like to have, keep the original branch unsquashed to preserve the history, but squash+merge on top of master, is not viable, as git will bring up all the history at every merge attempt. Instead you will have to start using cherry-pick and similar to make copies of "new" commits every time, and the hassle is just not worth it.
For completeness, there is a way to record a merge commit without using the normal merge command with git, in essence recording that relationship. However, this involves telling git explicitly how things are related, and how to disregard double changes, again the hassle is not worth it (in my opinion), so I won't elaborate on this here. Suffice to say it involves git commit-tree
and is rather low-level.