A couple of sometimes demanded tricks when working with git
I want to share recipes for solving a couple of problems that sometimes arise when working with git, and which are not "directly quite obvious".
At first I thought I'd have to accumulate more recipes, but there's plenty of time for everything. I think if there is a benefit, then you can and a little
Merzhim old branches with minimal pain
Preamble. There is a main branch (
? master ), In which new features and fixes are actively committing; there is a parallel branch
feature , at which the developers swam for some time into their own nirvana, and then suddenly found that for a month they had not been with the master, and the measure "head-on" (head with the head) had become non-trivial.
(Yes, it's not about an ideal world where everything is right, there is no crime, children are always obedient, and even cross the street strictly by the hand with their mother, looking around carefully).
The goal: to zamerzhitsya. At the same time, to make it a "clean" measure, without any special features. Those. so that in a public repository in a branch graph two threads are joined at a single point with the message "merged branch 'master' into feature". And the whole "that's this" headache about how much time and effort it took, how many conflicts it was decided and how much hair at that graying there is no need to store.
The story. That in the gita you can edit the last commit with the key
- amend know everything. The chip is that this "last commit" can be anywhere and contain anything. For example, it might not be just "the last commit to the line branch" where you forgot to correct the typo, but also the merge commit from the usual or "octopus" merger.
- amend exactly the same rolls the proposed changes and "builds" the modified commit into the tree, as if it really appeared as a result of an honest merger and conflict resolution. Essentially
git merge and
git commit --amend allows you to completely separate "staking place" ("this commit in the tree will be HERE") and the content of the commit itself.
The basic idea of a complex merge commit with a clean history is simple: first, "place the space", creating a pure merge commit (regardless of the content), then rewrite it using
- amend , making the contents "correct".
"Pitch the place". This can be done easily by appointing a strategy that will not ask unnecessary questions about conflict resolution.
git checkout feature
git merge master -s ours
Oh yes. It was necessary before creating a "spare" branch from the head of the feature. After all, nothing really is not merged But let it be the 2nd point, not the 0th. In general, we pass not a non-merged feature, and now we honestly merge. In any accessible way, regardless of any "dirty hacks". My personal way is to look through the master branch from the moment of the last merge and evaluate possible problem commits (for example: corrected in one place a typo - not a problem.) Massively (on many files) renamed any entity - problematic etc.). From problem commits we create new branches (I do artlessly - master? master? master? etc.). And then we merge the branch after branch, moving from old to fresh and correcting conflicts (which, with this approach, are usually self-evident). Other methods suggest (I'm not a magician, I'm just learning, I'll be happy with constructive comments!). Ultimately, after spending (maybe) several hours on purely routine operations (which can be trusted to a junior, for there are simply no complicated conflicts with this approach), we get the final state of the code: all the innovations /fixes of the wizard are successfully ported to the feature branch, all relevant tests on this code went through, etc. A successful code must be zakommitan.
We rewrite the "success story". While on the commit, where "everything is done", run the following:
git tag mp
git checkout mp
git reset feature
git checkout feature
git tag -d mp
(I decrypt: with the help of the tag (mp - merge point) we go to the detached HEAD state, from there reset to the head of our branch, where at the very beginning the "place is stolen" by a deceptive merge commit. The tag is no longer needed, so we delete it). Now we stand on the original "clean" merge commitee; while in the working copy we have the "right" files, where everything needed is deadly. Now you need to add all the changed files to the index, and especially carefully look at non-staged (there will be all the new files that have arisen in the main branch). We add everything we need from there, too.
Finally, when everything is ready - we enter our correct commit into the reserved place:
git commit --amend
Hooray! Everything worked out! You can casually smash the branch into a public repository, and no one will know that you actually spent half a day of working time on this measure.
Select the part of the file, keeping the history of
The preamble: the file was encoded-encoded, and finally put it so that even the visual studio began to slow down, digesting it (not to mention JetBrains). (Yes, we are again in an "imperfect" world, as always).
Smart brains thought-thought, and identified several entities that can be spun off into a separate file. But! If you just take, copy a piece of the file and paste it into another - it will be a completely new file from the point of view of git. In the event of any problems, a search for history will unequivocally indicate only "where is this disabled?", Which shared the file. And it is necessary to find the original source not "for repression", but purely constructive - to find out WHY this line was changed; which bug it fixed (or did not fix any). It would be desirable, that the file was new, but thus all history of changes nevertheless remains!
The story. With some slightly annoying edge effects, this can be done. For definiteness, there is a file called
file.txt , from which I want to highlight a part in
file2.txt . (and at the same time preserve the story, yes). Run this snippet:
f = file.txt; f1 = file1.txt; f2 = file2.txt
cp $ f $ f2
git add $ f2
git mv $ f $ f1
git commit -m "split $ f step ? converted to $ f1 and $ f2"
As a result, we get the files
file2.txt . They both have exactly the same story (real, like the original file). Yes, the original
file.txt at the same time I had to rename it; This is the "slightly annoying" marginal effect. Unfortunately, I can not find a way to save the history, but in order not to rename the original file, I could not (if anyone could - tell me!). However, the gith will endure all; no one now prevents a separate commit from renaming the file back:
git mv $ f1 $ f
git commit -m "split finish, rename $ f1 to $ f"
file2.txt gilt will show the same story line as the original file. The main thing - do not merge these two commits together (otherwise all magic will disappear, tried it!). But at the same time, nobody prevents editing files right during the separation process; it is not necessary to do this later with individual commits. And yes, you can allocate many files at once!
The key point of the recipe is to rename the source file to another one in the same commit where the copy (copy) is made from (and possibly edited). And let this commit in the future live (never drank with reverse renaming).
It may be interesting