|
|
|
|
|
by comment500
1457 days ago
|
|
Another useful thing if you need to backdate commits for whatever reason are the GIT_AUTHOR_DATE and the GIT_COMMITTER_DATE environment variables, upon executing git-commit they'll override these fields in the commit to whatever date, time and timezone you specify. I use this sometimes when I'm making some previously private work public, and am redoing the commit history to make more logical sense to others who may read it. Also useful are git-fast-export and git-fast-import, if you really need to delve into the inner details of a commit. For example, I had three separate but related git repos that I needed to merge, so I created a new repo with separate branches to hold each repo, merged everything manually, committed that to a new branch, then used export/import to edit the commit to have the tips of the three other branches as its ancestors. Maybe there's a better way to do this with other git commands but I found it easier just to delve in and edit the commit data manually. |
|
Similar story here: at a previous job we had a monorepo with a Rails app and Rails engines extending its appearance and behaviour and per customer.
At some point the architecture became problematic and we moved towards a shell app, a core engine, and extension engines depending on the core.
We refactored code to that end, and "forked" the original monorepo into multiple clones, one per component, then stripping the other components in each one, ending up with 1:1 repo/gem/component. This worked for a while, easing a lot of issues we had previously, allowing for proper dependency expression, independent development and releases... Everything was great and we lived happily ever after.
Then much later we hit a snag (I can't exactly recall what that snag was, IIRC it was not technical but organisational). So we looked at options and decided to merge into one single repo again. To that end we could do a big code drop, starting afresh, but (again I can't recall why) there was a need/requirement to keep at least some git history.
But at that point, "some" ends up ~== "all". So I devised a plan.
I git init'd a blank repo, added each one of the repos as separate remotes, and fetched each of them. Thus all git objects of all these repos were present. Then I checked out each one of these remote's master as a separate branch, created a subdirectory with the component name, moved every file for that checkout into that directory, and committed that. This way a) each project could live separately in the new monorepo and b) there would be no conflict for a merge.
Then came time for the merge. Two options: a) perform N merges subsequently or b) perform an octopus merge. a) just felt wrong and ugly, so I decided to try if I could work b) out, but I ended up not being able to achieve that with porcelain commands as git was being too smart and attempted to look into the history for some reason I can't recall which produced senseless conflicts (IIRC git merge isn't entirely assymetric)
So, since merge commits are merely commits with more than one parent I figured out I may be able to do that with plumbing commands instead. So the steps were:
- for each branch, check out content (but without moving the current HEAD, so, actually, export the git tree corresponding to a specific ref/sha)
- add all that to the index
- create a merge commit object with each branch's sha as parent
And it Just Worked, with the bonus that since up til the commit where we forked, each branch had common parents that were untouched, and commit history properly zipping up, by git's design, which is really a DAG of commit objects.