1

I use a git repo to store my vimwiki, a personal wikipedia which houses diaries, some latex, but mostly markdown wiki files. I then have this repo locally stored on two machines, one at work, and one at home.

To complicate matters each computer has it's own local database, or DVCS database (Something I just learned).

┌─────────────┐    ┌─────────────┐
│ repo folder │    │ repo folder │
│             │    │             │
│             │    │             │
└─────────────┘    └─────────────┘
       ▲                  ▲
       │                  │
       │                  │
       │                  │
       │                  │
┌──────┴──────┐    ┌──────┴──────┐
│ computer A  │    │ computer B  │
│             │    │             │
│ database    │    │ repo DB     │
│             │    │             │
└─────────────┘    └─────────────┘

On each computer, for a long time I typically ignore commits and make changes freely, and the two repos are isolated from each other. For the most part this isn't a problem, but vimwiki diary structure contains a funny quirk that does create a conflict. When running VimwikiMakeDiaryNote, the plugin command, vimwiki creates a filename using date +%F under the diary folder, and if I use this command on each computer within 24 hours git will give me a merge conflict for that day. Follow this pattern for a year and I will have 364 merge conflicts on new year's day.

This is presumedly because they have no common ancestor, are the same file on both systems in namespace, and have no common lines. To combat this nearing catastrophe I have read up a bit on merge strategies, and followed answers to other stack overflow posts, specifically this ( strategy for git and append-mostly files ), developing a schema to append differing files to one another.

This involves the following. (1.) Creating a merge driver in your git config .git/config, and (2.), specifying rules for that merge driver in a .gittattributes file.

path/.git/config ...
[merge "aggregate"]
        name = aggregate both new sections for diary entries
        driver = git merge-file --union -L %P %A %O %B
path/to/.gitattributes ...

test.txt merge=aggregate
diary.txt merge=aggregate
diary/* merge=aggregate

At this point the history of the two repos is important. The database on Computer A, as it's name suggests, has been given many more commits than it's counterpart on B. For this reason I wished to merge only the files under folders poetry, and diary.

/path/to/vimwiki/
.
├── bin
│   └── lib
├── poetry
│   └── bin
├── diary
├── searches
├── images
├── latex
│   ├── first\ pgf\ gnatt\ chart
│   ├── flowcharts
│   ├── images
│   ├── sections
│   ├── tables
│   └── venn_diagrams
├── lilypond
│   ├── htmloutput
│   └── tmp
├── mail
└── mathematics

After having staged the objective thus far, I made some preparations to merge the two repos. On computer B, I ran:

git checkout -b stem
git add .
git commit -m 'added entries since last log, including need diaries'
git push ~/.git-repos/vimwiki

On Computer A, I ran:

git checkout -b phone
git fetch ssh://XXX.XXX.X.XX/path/to/path/to/home/.git-repos/vimwiki stem:leaf

From this point I had created a branch to merge into, phone, and fetched the vimwiki repo into another branch leaf.

An interesting hurdle in this process was my desire to have the in congruencies visible after the merge. I searched for a way to create a driver to do this, and when I couldn't resolved a simple for loop that I suppose is part of the linux philosophy. It appended, suffixed foldmarkers to the files I need to merge.

for i in $(git --no-pager \
diff --name-only --diff-filter=AMCR \
phone..leaf -- ./diary | head -n 12 | tail -n 11);do
echo "$i";
gsed -i -e '1i\ %% B Diaries {{{1 %%' -e '$a\ %% 1}}} %%' $i
done

I then commited these changes to my leaf branch and was ready to merge them into the phone branch.

I had tested the git merge --no-commit on another repository going through the steps to make sure it respected the rules in my .git/config, and .gitattributes as I've shown above, but here I needed to only copy over files from the diary folder. For this I chose git merge --no-commit -X subtree=diary leaf

This is when I encountered problems, although the code worked as intended, appended the files in leaf to those in phone, it did not do so without behaving in two ways that weren't intended.

Firstly, it seemed to ignore the -X subtree argument and tried to merge in greedy fashion. I had thought that -X subtree would isolate only those files below the tree to merge. I have also tried this with the -s recursive option and it produces the same result: git attempts to merge greedily, staging every files that differs. Secondly, it complained that the merge failed. Asking me to fix the conflicts and then commit the result.

While testing the merge driver out on another repository the test exited with the message: Automatic merge went well; stopped before committing as requested. This was the result that I'd intended with my vimwiki repo.

Here is an example conflict message I receive:

CONFLICT (add/add): Merge conflict in diary/2023-06-02.md Auto-merging diary/2023-06-02.md

Some of the questions that I've asked myself are, does using optional arguments -s recursive or -X subtree prevent the merge driver from working. Presumedly, no, because although git ignores those options, it does append the files with the merge rule as expected.

I'd like to know how to get this two pronged objective of mine completed. (1.), merge using a merge driver, and (2.), do so while using a sparse, or subtree merge.

0 Answers0