lmi
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [lmi] Help combining git branches


From: Vadim Zeitlin
Subject: Re: [lmi] Help combining git branches
Date: Tue, 29 Sep 2020 19:53:12 +0200

On Tue, 29 Sep 2020 15:55:36 +0000 Greg Chicares <gchicares@sbcglobal.net> 
wrote:

GC> On 2020-09-20 23:24, Vadim Zeitlin wrote:
GC> > On Sun, 20 Sep 2020 22:37:51 +0000 Greg Chicares 
<gchicares@sbcglobal.net> wrote:
GC> [...]
GC> > GC>  git branch odd/foo
GC> > GC>  git reset --keep origin/master
GC> [...]
GC> > GC>    # ...and don't ever rebase that new branch
GC> 
GC> [...remove that "don't ever" comment?...]
GC> 
GC> > 1. Remove the comment: I think it's a bit too drastic, it might be useful
GC> >    to note that you shouldn't rebase this branch on master without using
GC> >    either an explicit branch name or --no-fork-point explicitly (you don't
GC> >    have to document both ways, just choose one you like best and it will
GC> >    be enough).
GC> 
GC> I just realized that I don't understand what you said. To rebase
GC> branch A on branch B, I should think it's necessary to name them
GC> both...so mustn't both branch names always be explicitly specified?

 No, the syntax for the usual form of git-rebase is

        git rebase [options] [<upstream> [<branch]]

i.e. both branch names are optional and it's, in fact, pretty common to
omit the latter one because doing this just means rebasing the current
branch. It's less common, IME, to omit <upstream>, but it still can be done
and then the default upstream configured for the current branch (remember
"git push --set-upstream"?) will be used.


GC> Would you mind looking over my virtual shoulder while I try to do
GC> something that seems awfully basic in git, and letting me know what
GC> you'd do instead? Here's the situation:
GC>  - I added branch valyuta/002;
GC>  - valyuta commits are carelessly casual; eventually, I'll want many
GC>    of their ultimate effects on master, but I'm sure I'll never want
GC>    their twisted history on master; and
GC>  - I've made changes there and to master, so they've diverged.
GC> Now there are some commits on master that I want on the branch (for
GC> pc-linux-gnu support). (There are also some commits on the branch that
GC> represent pure improvements that have nothing to do with 'currency',
GC> and I'd ideally like to perform on master, but that can wait.)
GC> 
GC> My first thought was to cherry-pick from each to the other, but I think
GC> that's not a recommended technique for a complex long-lived branch like
GC> valyuta/*, and I suspect it would make merging or rebasing harder, so I
GC> figured this would be a good time to try a different approach.
GC> 
GC> The first thing I tried was:
GC> 
GC> $git switch valyuta/002
GC> Switched to branch 'valyuta/002'
GC> Your branch is up to date with 'origin/valyuta/002'.
GC> $git rebase --no-fork-point master

 As you've noticed yourself below, "--no-fork-point" here is superfluous,
even if harmless.

GC> Applying: Change type of currency variables to 'currency' in headers
GC> Applying: Currency
GC> Using index info to reconstruct a base tree...
GC> M       ihs_acctval.cpp
GC> M       ihs_avmly.cpp
GC> M       ihs_avsolve.cpp
GC> Falling back to patching base and 3-way merge...
GC> Auto-merging ihs_avsolve.cpp
GC> CONFLICT (content): Merge conflict in ihs_avsolve.cpp
GC> Auto-merging ihs_avmly.cpp
GC> CONFLICT (content): Merge conflict in ihs_avmly.cpp
GC> Auto-merging ihs_acctval.cpp
GC> CONFLICT (content): Merge conflict in ihs_acctval.cpp
GC> error: Failed to merge in the changes.
GC> Patch failed at 0002 Currency
GC> hint: Use 'git am --show-current-patch' to see the failed patch
GC> 
GC> ...and already I'm in the Twilight Zone.

 It's not really surprising that there are conflicts, because changes in
the valyuta branch are very extensive and overlap with almost any other
change that could be done to these files and you've indeed done some
conflicting changes in the master in 1a54e490e (Clarify non-MEC solves,
2020-09-11), f7868401e (Avoid catastrophic cancellation, 2020-09-11) and
6a05bcee9 (Resolve a marked defect [338], 2020-09-12) respectively.

GC> The first of about forty commits came through okay, but the second
GC> failed, and all I have to work with is a monolithic patch file.

 No, it's just Git not being very helpful (although I admit I never
realized how confusing this hint could be before reading your message). You
may look at the entire patch, of course, and it can be sometimes helpful,
but the main thing you should look at are the files in the working
directory themselves.

[useless, as you already did this below, section kept for reference]

 If you open ihs_avsolve.cpp in Vim, for example, and search for
"^[<=>]\{7\}\( \|$\)" (without quotes), you will find all conflict markers
in it. And you "just" have to edit the file to get rid of the markers and
replace the text inside them with the correct version.

 In this case it should be pretty simple, as the changes in valyuta branch
are superficial, i.e. you should just take the master version and then add
.c() or whatever is needed to them.

[end of useless section]

GC> Maybe I could handle that for one commit, but not for forty,

[advanced section, please feel free to skip at first reading]

 I don't think you're going to get the same conflict again, but if you do,
I'd like to mention that there is a very useful (but somewhat surprising,
which is why it's turned off by default, I guess) git command with an
extremely clear name "rerere". In fact, expanding its name to "reuse
recorded resolution [of conflicted merges]" probably doesn't make it much
more clear. But using it is very simple: just run "git rerere" (without any
arguments) once and Git will remember your merge conflict resolutions. This
means that when/if you need to resolve the same conflict again as Git will
automatically apply the same resolution to it. This is a bit confusing
because it will still tell you about the conflict, but then there won't be
any conflicts when you look at the file. But it's also, of course, very
useful, because you won't need to apply the same resolution manually again.

[end of advanced section]

GC> so I abort and try a different command:
GC> 
GC> $git rebase --abort
GC> $git rebase master
GC> 
GC> But that has the same effect. (I guess that's because the
GC> '--no-fork-point' option is implicit when I name master explicitly).

 Yes, absolutely.

GC> After aborting again, I try '--onto' because it sounds like it would
GC> be perfect for my purposes, according to this diagram:
GC> 
GC> 
https://stackoverflow.com/questions/33942588/difference-between-rebase-master-and-rebase-onto-master-from-a-branch-deri/33942992#33942992

 No, --onto is very useful when you want to transplant changes on a branch
"Topic" split from some branch "Base" to another branch "New base". You
don't need anything like this here, i.e. your old base and new base are one
and the same (master).

GC> | After a standard rebase (without --onto master) the structure will be:
GC> |
GC> |            o---o---o---o---o  master
GC> |                |            \
GC> |                |             x'--x'--x'--x'--x'--o'--o'--o'  B
GC> |                 \
GC> |                  x---x---x---x---x  A
GC> |
GC> | ...where the x' are commits from the A branch. (Note how they're now 
duplicated at the base of branch B.)
GC> |
GC> | Instead, a rebase with --onto master will create the following cleaner 
and simpler structure:
GC> |
GC> |            o---o---o---o---o  master
GC> |                |            \
GC> |                |             o'--o'--o'  B
GC> |                 \
GC> |                  x---x---x---x---x  A

 You don't have branch "A", just branch "B" (== valyuta/002).

GC> $git rebase --abort
GC> $git rebase --onto master

 BTW, here you did omit both <upstream> and <branch>, showing that it can
indeed be done. But it's really not useful to use master as both the --onto
value and the <upstream> argument.

GC> First, rewinding head to replay your work on top of it...
GC> 
GC> I was surprised that it just stopped there,

 Yes, me too... If I do it here, it says

        Successfully rebased and updated refs/heads/valyuta/002.

 Let's try to understand what it's supposed to do: git-rebase starts by
stashing (not using git-stash, but using a similar mechanism) all changes
between <upstream> and <branch>. In your case, there are _no_ such changes
because your branch is the same as its upstream origin/valyuta/002, as
shown by "git switch" output in your email.

 Next it resets the branch head to "master", because you've used "--onto
master". And finally it reapplies all the stashed changes on master --
except that there are none, so it doesn't actually do anything.

 IOW the effect of "git rebase --onto master" when your branch is
synchronized with its upstream is just to make your branch the same as
master. Which is almost surely not something you'd like to do.

GC> $git pull --no-ff

 FWIW I still recommend not using pull to do merges implicitly. I.e.
instead of the above I'd do

        $ git fetch
        $ git merge origin/valyuta/002

which is exactly the same thing, but more clear.

GC> Auto-merging ihs_avsolve.cpp
GC> CONFLICT (content): Merge conflict in ihs_avsolve.cpp
GC> ...
GC> Automatic merge failed; fix conflicts and then commit the result.
GC> 
GC> This seems a lot better than editing git-am patches. I fix the
GC> (very few, and minor) conflicts in 'vim'

 Oops, so you already know how to do this. I should have read more
carefully during my first pass, as it is, I've just marked my explanations
of how to resolve conflicts above as useless.

GC> and forge ahead:
GC> 
GC> $git add gwc/develop1.txt ihs_avmly.cpp ihs_avsolve.cpp
GC> $git status
GC> On branch valyuta/002
GC> Your branch and 'origin/valyuta/002' have diverged,
GC> and have 42 and 112 different commits each, respectively.
GC>   (use "git pull" to merge the remote branch into yours)
GC> 
GC> All conflicts fixed but you are still merging.
GC>   (use "git commit" to conclude merge)
GC> 
GC> I've already pulled, so now I'll commit:
GC> 
GC> [valyuta/002 abf690c4] Merge branch 'valyuta/002' of 
git://git.sv.gnu.org/lmi into valyuta/002
GC> 
GC> And it looks like I'm all done:
GC> 
GC> $git status
GC> On branch valyuta/002
GC> Your branch is ahead of 'origin/valyuta/002' by 43 commits.
GC>   (use "git push" to publish your local commits)

 Well, you've successfully merged origin/valyuta/002 into your local
valyuta/002 branch that you had previously reset to master. This is
different from merging origin/master into your local valyuta/002, but the
end result is the same, and if you plan to delete this branch later anyhow,
i.e. it's just temporary, there is no real harm in doing it like this.

GC> But I want to understand what happened, and here I'm confused.

 All the confusion is due to "git rebase --onto master" which reset your
local valyuta/002 branch to master. Ideally you shouldn't have done it.

GC> $git log --graph --oneline --all -200
GC> 
GC> I think it'll be clearest if I present extracted blocks of '--graph'
GC> output in their original order. But that means I must ask you to skip
GC> ahead to "rbegin()" below and read my interleaved annotations backward.
GC> 
GC> [this is the "rend()" of reverse-chronological '--graph' output]
GC> 
GC> Here's my burning question: dare I push this?

 You need to test it locally first, of course. But if it builds/passes
whichever tests are supposed to pass at the current stage of this work,
then the answer is "yes".

GC> If I do, might I destroy valyuta/002 in some irrevertible way?

 No, it will look slightly strangely due to the merge being done in the
wrong order, but it doesn't really matter and definitely doesn't destroy
anything.

GC> Should I now do something like:
GC>   git switch --create valyuta/003
GC>   git reset --keep origin/master
GC>   git push --set-upstream origin valyuta/003
GC> to keep valyuta/002 intact, in case I try working with whatever I've
GC> just created and find that it's ruined? Well, let me do that anyway,
GC> because it has to be safe, even if wasteful.

 It's safe and not even wasteful (branches don't cost anything in Git), but
it's not very useful neither.

GC> *   abf690c4 (HEAD -> valyuta/002) Merge branch 'valyuta/002' of 
git://git.sv.gnu.org/lmi into valyuta/002
GC> |\
GC> | * 5fb74f1b (origin/valyuta/002) Prefer cents() to m()
GC> | * 89b2c7ab Fix incorrect documentation
GC> | * 2f32980d Do away with unused return value #5
GC> | * 94197291 Do away with unused return value #4
GC> 
GC> Here the flow marked with '|' crosses over, exactly at origin/master.
GC> I guess that's because git wisely avoids rewriting a pushed master
GC> branch.
GC> 
GC> | * 360060cb suppress failing assertions
GC> | * 08560813 improve currency
GC> * | f94663e9 (origin/master, origin/HEAD, master) Add a script that's not 
ready to use
GC> * | 3299d705 (xanadu/master) Sidestep a vim regression
GC> 
GC> I had switched back to master and done some work there.
GC> 
GC> * | b99f6ac4 Include all 'sample*' product files in a fardel by default
GC> * | 5d8c62c8 Add another little git recipe
GC> | | * ef5db171 (origin/odd/string_db, odd/string_db) Allow product lingo to 
vary by state, gender, and so on
GC> | |/
GC> |/|
GC> * | bddd7c3d Improve gcc warning options
GC> * | f4d9e2e8 Restore last position in vim
GC> * | 193164fb Improve a git-branch recipe
GC> * | 6a05bcee Resolve a marked defect [338]
GC> * | f7868401 Avoid catastrophic cancellation
GC> * | 1a54e490 Clarify non-MEC solves
GC> 
GC> Here, I tried some ideas that weren't all good enough for valyuta/002,
GC> so I stuffed them onto a new throwaway branch.
GC> 
GC> | | * 657673a0 (valyuta/002x) flailing
GC> | | * 9913071e some good ideas here
GC> | | * dfab1b3d round.c()
GC> | | * 58acbbca round.c()
GC> | | * b067f1cf round.c()
GC> | | * 82c14bcb improve currency
GC> | |/
GC> | * 9bee994b use currency type
GC> 
GC> Okay, this is the original point where valyuta/002 came into existence.
GC> I thought 'git rebase --onto master' was instead supposed to jump to
GC> the HEAD of master and then replay all of valyuta/002's commits onto
GC> it: isn't that what the diagram from stackoverflow above implies?

 I hope the explanation above makes it clear, but actually that rebase just
didn't do anything at all. This can, BTW, be seen by the fact that git
status above tells you that your branch is "ahead of origin/valyuta/002"
and not "diverged" from it: if you had done any real rebasing, they would
have diverged because your local commits would have been rewritten.

GC> But whatever it's doing here looks good enough for my present goal.

 Yes, it is.


 FWIW the best would have probably been to just return to your original
valyuta/002 state, i.e. "git reset --keep origin/valyuta/002", and then
redo "git merge master" (or origin/master if it's different from your local
master, but I think they're the same). Unfortunately right now this means
that you'd have to re-resolve the same conflicts again because you hadn't
enabled rerere, so it's not worth doing it. But this is how it would
normally be done for a long-lived branch like this.

 BTW, the reason I recommend merge and not rebase is rather egoistical: I
already have copies of your valyuta/002 branch in several places here,
which I'd need to git-reset if you change the commits on it using rebase
while if you use merge, I can just update them as usual (which is the
reason for which it's not recommended to use rebase with publicly visible
branches).

 Hope this helps, but please let me know if you have more questions, it's
absolutely normal to have them when starting working with branches.

VZ

Attachment: pgpEW47gi5yHa.pgp
Description: PGP signature


reply via email to

[Prev in Thread] Current Thread [Next in Thread]