
Git Tricks: Avoiding merge when dealing with remote conflicts - danzanzini
https://goiabada.blog/git-tricks-avoiding-merge-when-dealing-with-remote-conflicts-52c175e526e6
======
languagehacker
I like how straightforward this blog post is. The graphics are very helpful in
getting the point across.

Unfortunately, this is dangerous advice in a reasonably sized engineering
team.

The assumption of pushing to master is kind of a dangerous one. Don't do that
unless it's a project that is yours and yours alone.

If you have a change you want to get in, consider a pull request. Even if you
don't need it reviewed, it generally encourages you to use a separate branch.

If you find that you have a conflict with origin/master, you can then easily
rebase your changes. Because of the nature of refs in git, you can also create
a separate branch before rebasing so that you don't have to worry about about
losing anything, or just to diff what you ended up changing after a
particularly hairy rebase.

So yes in short, you can use rebases to avoid merge commits. But the use case
here and the workflow described is not one I would recommend for teams using a
centralized origin or a stable head of master.

~~~
harg
I didn't get the impression the article assumed pushing to master. It was more
a case of you're working on a feature branch and another dev pushes to that
same feature branch before you push your local commits.

I agree that even in larger teams you shouldn't face this problem in the first
place if devs work just on their own branches instead of sharing. Rebasing is
also a great way way to avoid merge commits.

------
lazulicurio
Personally, I prefer to never use git pull, and instead use git fetch then
manually merge or rebase as appropriate. It's one more command, but I find it
helps me visualize exactly what I want to happen.

~~~
rinchik
> It's one more command, but I find it helps me visualize exactly what I want
> to happen

are you saying that git pull can produce some unwanted artifacts? (e.g. not do
exactly what you want it to do?)

~~~
lazulicurio
> are you saying that git pull can produce some unwanted artifacts? (e.g. not
> do exactly what you want it to do?)

Not in the sense that git pull has unpredictable behavior, but I like being
able to view the remote history and manually diff against my local branch if
there are changes that have been made.

~~~
jolmg
Though it's still better to do fetch when you want to compare first, pull
outputs the ranges of commits that it's pulling for each branch, so you can
just copy and paste that into a diff or log command, too.

I say this because I used to just ignore the output, but then I found that
it's useful, too.

So, if git-pull outputs

    
    
       b3b2007..9bad624  master       -> origin/master
    

I can do `git diff b3b2007..9bad624` or `git log b3b2007..9bad624` to see what
I just pulled.

------
mcv
`git rebase` is fairly useful when differences are small. Rebase changes the
history of your own commits. When that history is long (you've got a lot of
commits that are not in the other branch), you'll end up resolving the same
conflicts over and over and over again, and in that case, a simple merge is
much less painful.

The only real advantage of rebasing is that it keeps your history linear. A
history with merges is harder to navigate. But at some point, merges become
unavoidable, and the larger the differences are, the more important it becomes
to use merge instead of rebase.

And as any time traveler knows, changing history comes with risks.

------
sam36
Any good git "best practices" doc will inform the user to never use rebase on
a shared remote repo. The details are given, but sadly I can never really
follow the logic. Rebase always sounds like a good idea (imo). And being the
only one to use rebase on my team... I've had several instances where I go to
rebase a remote branch onto my local work, and I'm met with tons of
conflicts.. except the conflicts are all my own commits from a later time.
I've never really figured out why, but I've since just stopped using rebase
unless I really know there is nothing funny on the remote branch.

~~~
danzanzini
The conflicts from a later time happens because rebase applies one commit at a
time.

If you solve a conflict from your first commit by applying changes that were
made only in the later ones, you'll need to solve this same conflict again and
again.

~~~
sixstringtheory
If resolving the same conflicts over and over again in many commits gets
onerous enough to make it worthwhile investing a little time learning another
part of git, you can check out git-rerere (Reuse recorded resolution):
[https://git-scm.com/docs/git-rerere](https://git-scm.com/docs/git-rerere)

~~~
sam36
What the heck

------
thiht
Rebase is not a "trick", it's one of the basic features of git.

If you consider it's a trick, please don't use it, it means you don't
understand it yet.

~~~
fcfl
By that logic there are no tricks, there are only ever features, basic or
otherwise.

If you consider sticking so much to literality, please don't use languages, it
means you don't understand them yet.

------
kirke
Another excellent resource on a similar vein: [https://git-
rebase.io/](https://git-rebase.io/)

Written by Drew Devault, author/maintainer of swaywm, sourcehut and others.

------
rinchik
Rebase isn't really a trick though.. is it?

It just a normal workflow and, sometimes, housekeeping in your small "feature"
branch.

~~~
rumanator
Rebase os also a good way to wreack havoc in a repository, both local and
remote.

~~~
chopin
Not in remote if you disallow force-push. For local I agree. For hairy rebases
I normally set a marker tag (easier than to comb through the reflog). Then it
is easy to reset the branch to its initial irrespective of what you've done.

~~~
mcv
Quite the opposite, sometimes rebasing means you _need_ to force-push, and not
doing so wreaks havoc.

Imagine I'm working on my own feature branch, pushing regularly to remote. I
make my pull request to develop, but develop is ahead of me and my pull
request can't be merged. So I pull develop, rebase my own local commits on top
of that, and push the result in my feature branch to remote, which contains
some of my old, un-rebased commits. The push is refused, and instead either
git or intelliJ automatically pulls from the remote feature branch into my
local branch, merging with no problem, so it can be pushed again.

It works, but not all my commits occur two times in the history, and that's
bad.

I suspect it's not git doing this but IntelliJ, but it's a popular IDE, so
this can easily happen to anyone if you're accidentally rebasing commits you
already pushed.

Force pushing prevents the problem, but you're probably better off not
rebasing in the first place. Don't change history that also exists in another
location.

------
darepublic
This is not a guarantee of no merge conflicts. Sometimes conflict cannot be
avoided, you'll need to deal with it sooner or later

~~~
gordaco
And depending on your commits, you may need to solve conflicts several times
(rerere alleviates this, though).

Also, isn't this very basic? I thought pull --rebase was one of the most basic
commands everyone learned while getting acquainted with git.

~~~
ghshephard
I started using git two years ago, and when I first asked all our senior
engineers about it (after reading an article or two on HN about best
practices) - the advice I was given was, "Never rebase." Among the more Jr.
Engineers there was this feeling that it was a dangerous command that should
be steered clear of, and the typical approach, of branching off of remote
master, making changes, pushing to your branch, testing your code in the CI/CD
environment (with appropriate unit/integration tests), and then issuing a PR
to be reviewed and merged into master was the appropriate use of git.

I don't think I've ever seen anybody (except very advanced git users) ever use
"rebase" \- so, no, I don't think I would consider it a basic command. Despite
using git dozens of times every day, I don't believe I've ever rebased outside
of a test-environment to see how it worked.

~~~
northwest65
I'm far from an advanced user, but I use rebase fairly regularly for long
running feature branches. As the parent branch continues along, I periodically
rebase complicated feature branches so that I'm doing a number of small easy
to handle conflict resolutions, rather than one almighty one at the end. I've
never had any trouble, and I'd never considered it to be anything out of the
ordinary or anything other than basic!

~~~
DuskStar
Huh. Personally, I merge master into my feature branches to keep up to date
instead of rebasing - I find it helps the tree match the actual actions that
were taken.

------
gouggoug
And

    
    
      git config --global branch.autoSetupRebase always
    

will ensure that all your _future_ branches are automatically setup to
`rebase` instead of `merge` on pull.

~~~
anon9001
You should want those merges so that you can later bisect where a problem was
introduced. If you start/end each day with a merge and leave the merge commits
in, and you notice something goes wrong later, it's much easier to trace where
the problem is.

Rebase definitely satisfies my OCD and makes everything look pretty, but it's
actually worse.

~~~
kazinator
I can't make sense out of your comment.

The concept of "git bisect" is inherently predicated on a simple linear
history consisting of commits that were applied on top of each other. We are
searching for the bad one which flips the test case from "good" to "bad".

If you throw an ugly graph into it and merges, it's not even comprehensible.
Say that instead of _git bisect_ we just go backwards, commit by commit,
starting at HEAD. Oops, we encounter a merge commit: which way do we go now to
find the breakage? Parent[0], or parent[1]? Or [2] ...? What is the correct
traversal order? Do we go down [0] first? At which commit do we backtrack and
try going into [1]?

There is absolutely no issue with _git bisect_ over a repository where you
have the unmodified upstream commits, and your own local ones rebased on top
of them. It nicely finds the bad commit in one or the other.

Now of course the rebase rewrites your code by merging it (in the real sense
of the word). If you discovered the bug yesterday (and it's in your own
commits), then you rebased this morning, you're now chasing that bug over
different commits that have been subject to auto-merge and maybe even manual
conflict resolution. Oh well; yesterday was yesterday! Repro the bug with the
newly rebased repo and bisect away.

Or, here is an idea: if you're chasing a bug, don't fetch anything and don't
rebase! Right? If you've repro-d a bug, you don't want to start changing code,
so the last thing you want is to bring in reams of changes from other
developers. I've never rebased while chasing a bug with git bisect; I stopped
what I was doing until I found it, in the nice linear history as it existed at
that point.

If external forces cause you to drop what you're doing and rebase while you
have some bug, what you can always do is "git tag bug-hunt" to set a bookmark
where you were. Do what you have to do, and then when you pop your stack,
return to that tag and debug from there. You can find the original bad commit.
If it's in your uncommitted work, you can cross-ref it to the latest rebase.
(There is a chance it might not exist any more; e.g. upstream changes made
some of your local code obsolete, so you threw them away in the rebase, and
the problem was in those discarded changes.)

~~~
anon9001
If you have a long running branch, you're going to have conflicts. When you
resolve those conflicts during a rebase, you lose your history of the conflict
resolution.

What you're thinking of as a "simple linear history" is actually not. Notice
that when you rebase there are new hashes for each of your commits.

If you're rebasing your branch with master and resolving merge conflicts along
the way, you're going to get bit eventually. Even if you're somehow not
dealing with merge conflicts, you're losing your ability to look at the git
log and figure out when the last time you synced up with master was.

~~~
kazinator
You can have conflicts even if you have a single-commit local change, that's
less than an hour old.

Long running branches are a bad idea. Try not to do that.

Not everything in a long running branch can be merged; selective things will
end up cherry-picked (which is a generalization of rebase). Suppose the trunk
maintainers like 75% of what was done in the branch. They might go through the
commits one by one and take what is relevant.

When you merge a branch, and want to follow a good SCM process, you must treat
it as a closed topic; do not commit anything more to that branch. Start a new
branch, even for a closely-related continuation of the same work.

The correct way to merge a branch is to rework every single commit onto the
new baseline. That is true whether that rework is represented as a merge, or a
cherry-pick. Therefore, we can't avoid the issue that every change turns into
a new object with a different hash.

What we can do is put tracking information in the commit message to correlate
these together. The Gerrit review system uses a Change-Id: line for this. Each
commit has random ID which is not a content digest; it stays the same across
cherry picks.

If I'm debugging a problem on trunk T, across some commits that came from a
branch R, I do not care what those commits looked like in R. Only in a very
rare case will I be interested how a change looked like there. I want every
commit that came from R to be reworked on T, as if it was originally developed
on T. If I find the problem and have a hunch that it was introduced during the
merge, then I might out of curiosity find the commit's cousin in the original
R branch just to see how it looked like there. Other than that, I'm shipping
T, not R, so that's what I'm debugging.

