-2

Ps. I don't want to perform a 3-way diff.

There are different settings in computer programming. Not everybody's job is part of devops, github, agile startups or some canonical open source workflow, but can still be working with programming. In our specific setting, we need to actively participate in each contribution independently of additional tools other than git. That said, once the changes are highlighted and editable, free from a cumulative process like Interactive Staging, one can use their local tool of choice to solve the 'conflicts' (actually, 'accommodate the changes').

Although there are some questions that sound similar to this one, none of them, neither their answers, address the general problem, which is useful not only for code in some contexts, but is specially important for documentation of code or other kinds of versioned text.

Say we have two branches main (master in my git version) and friend (or origin, depending on each particular case). I'd like to be able to accept, reject or edit each incoming change, since each one can be a reviewer of the text, and need to incorporate the changes/suggestions without changing the original intent. Another example is a supervisor receiving code from a student or the other way around. To be clear, this is not in a context of a bigger workflow, like GitHub, pull requests, etc. It is just pure git. Well, that's enough about the existence of such setting for who may be used to a different daily use of VCS.

Example:

mkdir force-conflict
cd force-conflict
git init .
echo -e "a\nb\nc\n" > file.txt
cat file.txt
# output:
a
b
c

git add .
git commit -m "First message"

Simulating friend's contribution:

git checkout -b friend
# output:
Switched to a new branch 'friend'
echo -e "d\n" >> file.txt
cat file.txt 
# output:
a
b
c
d

git add .
git commit -m "Contributions from friend"

Ok, my friend contributed. Going back to my code (master or main)...

git checkout master
# output:
Switched to branch 'master'

...if I want the incoming changes (e.g., the added last line) to appear as conflict, it is not enough to use the solutions like the one given in the next paraghaph. And a merge will automatically include it without alarm.

Some answer to other similar question (that I couldn't find anymore) pointed out about interactive mode in git, but that is a complicated process of iterating over all changes sequentially and is not portable to other tools/IDE (e.g., Intellij's merge utility or other IDE/diff tools). Other solution seems to ignore changes on a single branch. Others just mark entire files as conflict, but do not specify them internally with markers <<<.

dawid
  • 663
  • 6
  • 12
  • I don't understand why you couldn't just do `git diff master friend` prior to merging `friend` into `master`. Does it have to appear as a _conflict_ or is it enough to have a diff you can review? if the latter, then comparing the branches with `git diff` seems to fit the bill. – Turix Dec 28 '20 at 15:05
  • I has to appear as conflict, for me to be able to use tools like intellij (maybe VStudio also, and others). Otherwise, I would have to have a open file to spot the changes and open another file to make the edits. The setting is easier to understand without the premise that `the changes will be mostly correct`, but instead that `every change must be edited`. – dawid Dec 28 '20 at 15:10
  • E.g., a teacher receiving contributions from a supervised student. There are many diverse settings in programming. – dawid Dec 28 '20 at 15:13
  • If what you want is to check every single line of code that is changed, you can either go through the separate revisions on the other branch.... _or_ check a diff (with 3 dots, just in case)... that's what goes on in projects like linux and git. They check every _revision_ that is sent their way. – eftshift0 Dec 28 '20 at 15:45
  • @eftshift0 I need to accommodate every change. To just see them is not enough. From the feedback I received so far (more than a year), it seems like git merge is lacking the concept of 'changes' in the sense that it automatically consider we are only interested in 'conflicts' . While this is reasonable for some settings, there is no reason to not have it as the highest level of control while merging. `git merge -s mark-all-changes origin`. – dawid Dec 28 '20 at 16:04
  • So, what you are asking is more or less something like `git merge --no-commit whatever` so that you can then check `git diff HEAD`? – eftshift0 Dec 28 '20 at 16:37
  • ... or use the IDE to check all files that have changed (after running `git merge --no-commit`). – eftshift0 Dec 28 '20 at 16:46
  • I think this question may be a victim of the [XY Problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). You are assuming you need conflicts to view and edit the same file, but that may not be true. The duplicate link is close to what you want. [This answer](https://stackoverflow.com/q/8640887/184546) may be even better. Essentially you just need `git merge --no-commit --no-ff friend-branch`. At that point the changes will be all staged. If you unstage them you'll have them all right there ready to be edited. – TTT Dec 28 '20 at 23:19
  • @TTT `git merge --no-commit --no-ff friend-branch` doesn't solve my problem. It is easy to see that, by comparing what my answer do and what diff does. (un)Staging doesn't help with going through and editing the changes. Anyway, I am happy with my solution and it is a pity it is not available for others. – dawid Dec 29 '20 at 14:10
  • @olyk I just looked at your answer. I think the end result is the same (from a code POV) if you use the merge command, but, I see in your answer you have the original author commits, and then your modifications as a new commit on top of it, which I agree is better than just completely re-doing the original work. I do see some of the benefit of using conflict resolution tools for every line of code as well, as it's easier to pick left or right or edit. It feels super clunky overall (which is probably why it's getting DV'd), and could likely be greatly simplified, but I guess it works... – TTT Dec 29 '20 at 16:08
  • @TTT Thanks for the consideration and feedback! – dawid Dec 30 '20 at 21:34

1 Answers1

-2

We recreate the two branches (friend and master) from a zeroed one (called here ether), so every change will be a conflict, effectively nullifying temporarily the common ancestor used by the 3-diff algorithm. In the end, just the external contribution and our changes will appear in the history, as expected.

git checkout master 
# output:
Switched to branch 'master'
git checkout -b ether
# output:
Switched to a new branch 'ether'
git rm -r .
# output:
rm 'file.txt'
git commit -m "Simulating an empty common ancestor"
git checkout -b master-from-empty
# output:
Switched to a new branch 'master-from-empty'
git checkout master .
git commit -m "Recovering old content as new content"
git checkout ether 
git checkout -b friend-from-empty
# output:
Switched to a new branch 'friend-from-empty'
git checkout friend  .
git commit -m "Recovering friend's content as ex nihilo"
git checkout master-from-empty
# output:
Switched to branch 'master-from-empty'

After master-from-empty receives changes from friend-from-empty, the merging process will result in a conflict as expected:

git merge friend-from-empty 
# output:
Auto-merging file.txt
CONFLICT (add/add): Merge conflict in file.txt
Automatic merge failed; fix conflicts and then commit the result.
cat file.txt 
# output:
a
b
c
<<<<<<< HEAD
=======
d

>>>>>>> friend-from-empty

We can edit the conflict as we wish...

echo -e "a\nb\nc\nd reviewed by me\n" > file.txt 
cat file.txt
# output:
a
b
c
d reviewed by me

... commit, and go back to master.

git add .
git commit -m "Accept/edit changes"
git checkout master
# output:
Switched to branch 'master'

We must take care of preserving friend's commit(s) and adding our changes on top of that.

git merge -X theirs friend           # To keep their authorship, before anything else.
git checkout master-from-empty  .    # Recover my edits.
git commit -m "Edit friend's contribution"
cat file.txt
# output:
a
b
c
d reviewed by me

git log
# output:
commit bbb62c91c72d880d65e330f1812d0685df6ea211 (HEAD -> master)
Author: xxx
Date:   Mon Dec 28 11:39:05 2020 -0300
    "Edit friend's contribution"

commit 724e67e1dc5befe85ee8e4371cb5ef415c774b5a (friend)
Author: xxx
Date:   Mon Dec 28 10:12:31 2020 -0300
    "Contributions from friend"

commit d06a87a4319d865ebbf352507021d74355a85d07
Author: xxx
Date:   Mon Dec 28 10:05:44 2020 -0300
    "First message"

Some clean up...

git branch -D ether master-from-empty friend-from-empty
# output:
Deleted branch ether (was 0e892f6).
Deleted branch master-from-empty (was 5677380).
Deleted branch friend-from-empty (was 0c7bc17).

Whether this can be safely put in a general bash script is a subject for another 2 hours of work.

dawid
  • 663
  • 6
  • 12