Skip to content

Commit 3312d8a

Browse files
committed
Add readme
1 parent 47e5be8 commit 3312d8a

File tree

1 file changed

+172
-0
lines changed

1 file changed

+172
-0
lines changed

README.md

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
# Composer JSON Git Merge Driver
2+
3+
The Composer JSON Git Merge Driver provides a mechanism to more effectively merge
4+
`composer.json` and `composer.lock` files that have been modified simultaneously
5+
in separate branches / development histories. The [custom git merge driver][merge driver]
6+
is activated when the composer JSON files require a merge more complex than a
7+
simple "fast forward".
8+
9+
## How it Works
10+
11+
The merge driver replaces git's standard merge algorithm only for composer's JSON files:
12+
instead of analyzing the files for changed lines, the JSON is parsed and the actual properties
13+
and values are compared for changes. As such, the merge driver is able to more gracefully
14+
handle most new, updated, and removed dependencies in your composer files. A merge conflict
15+
is only triggered when the version constraint, locked version number, or presence / absence
16+
of the same dependency has been modified in multiple development histories involved in the
17+
merge.
18+
19+
For instance, if a certain dependency is updated in one branch and removed in another,
20+
a merge conflict is triggered because it is unclear which change for that dependency is
21+
desired following the merge. However, if a new, different dependency is appended to the
22+
`require` section in both branches, the merge driver will understand that both should
23+
be kept, whereas the standard git merge driver would trigger a merge conflict because
24+
the same line has been edited in both branches.
25+
26+
More generally, all object data structures are merged gracefully and recursively, meaning
27+
other composer configuration properties in the composer JSON files (e.g. `extra` and
28+
`config`) will be merged properly as well—whenever changes are not ambiguous. In fact,
29+
the merge driver will work on any JSON file whose outermost data structure is an object.
30+
31+
### Composer Lock Handling
32+
33+
For the the lock file in particular, special handling is performed to minimize the potential
34+
for merge conflicts. For example, the locked dependency data is converted into an object
35+
for the purposes of merging and converted back to the proper data structure when the merge
36+
is complete. As well, the `content-hash` property is excluded from the merge, as the very
37+
nature of this property guarantees that two differing branches would have conflicting values.
38+
The content hash is used by composer to efficiently recognize that the `composer.json` file
39+
has changed, so the actual value is not necessarily important after a merge has occured.
40+
As a workaround for not being able to generate an accurate hash, the merge driver sets its
41+
own unique value for the hash to signal to composer that a change has occurred; the value is
42+
a simple message instructing you to run `composer update --lock` so composer can regenerate
43+
the real hash!
44+
45+
[merge driver]: https://git-scm.com/docs/gitattributes#_defining_a_custom_merge_driver
46+
47+
## Installation
48+
49+
The merge driver can be installed globally or per repo and activated globally or per repo,
50+
i.e. you can install the driver globally but only activate it on certain projects.
51+
52+
### 1. Download the merge driver.
53+
54+
Download the `merge.php` file from this repo. For example, to download to your home directory:
55+
56+
```sh
57+
$ curl -Lo ~/composer-git-merge-driver.php https://raw.githubusercontent.com/balbuf/composer-git-merge-driver/master/merge.php
58+
```
59+
60+
Make the file executable:
61+
62+
```sh
63+
$ chmod +x ~/composer-git-merge-driver.php
64+
```
65+
66+
Optionally, you can move the file somewhere in your `$PATH`, e.g.:
67+
68+
```sh
69+
$ mv ~/composer-git-merge-driver.php /usr/local/bin/composer-git-merge-driver
70+
```
71+
72+
### 2. Install the merge driver.
73+
74+
The driver is installed by informing git of its existence via a [git config][git config] file:
75+
76+
```
77+
[merge "composer_json"]
78+
name = composer JSON file merge driver
79+
driver = ~/composer-git-merge-driver.php %O %A %B %L %P
80+
recursive = binary
81+
```
82+
83+
To open the git config file for editing in your default command line text editor:
84+
85+
```sh
86+
$ git config -e
87+
```
88+
89+
By default, this allows you to edit the config file for the current repo, meaning the driver
90+
is only installed locally to the repo. To edit the global config file for your user, append the
91+
`--global` flag; to edit the system-wide config file, append the `--system` flag.
92+
93+
Copy and paste the block above into the config file and save it. Be sure to update the path if
94+
you downloaded the file to somewhere other than `~/composer-git-merge-driver.php`. If you moved the
95+
file somewhere in your `$PATH`, you can simply replace the path with `composer-git-merge-driver`.
96+
97+
For more information about git config files, refer to the [git documentation][git config].
98+
99+
[git config]: https://git-scm.com/docs/git-config
100+
101+
### 3. Activate the merge driver.
102+
103+
Finally, the merge driver must be activated for `composer.json` and `composer.lock` files via
104+
a [git attributes][git attributes] file:
105+
106+
```
107+
composer.json merge=composer_json
108+
composer.lock merge=composer_json
109+
```
110+
111+
To activate only for a specific repo, edit the `.git/info/attributes` file inside of the repo.
112+
To activate globally for your user, edit the `~/.gitattributes` file; to activate system-wide,
113+
edit the `$(prefix)/etc/gitattributes` file (e.g. `/usr/local/etc/gitattributes`). Copy and
114+
paste the block above into the config file and save it. In some cases the file may not yet exist,
115+
in which case you can simply create the file at the aforementioned path.
116+
117+
For more information about git attributes files, refer to the [git documentation][git attributes].
118+
119+
[git attributes]: https://git-scm.com/docs/gitattributes
120+
121+
## Usage
122+
123+
The merge driver is automatically invoked any time a `composer.json` and/or `composer.lock` file
124+
must be merged in a repo where the driver is activated. If there are any merge conflicts within
125+
the files, the standard git merge conflict procedure kicks in: you will be alerted of which file(s)
126+
contain conflicts, and conflicts are denoted via the standard conflict markers (e.g. `<<<<<<<`).
127+
128+
If there are no conflicts and the merge can be applied cleanly, the merge completes as normal.
129+
However, a changed lock file results in a non-standard `content-hash` value (whether there are
130+
conflicts or not). While there should be no harm in leaving the hash value as-is, it's best to
131+
let composer regenerate the hash. To do so, simply run:
132+
133+
```sh
134+
$ composer update --lock
135+
```
136+
137+
The value of the hash actually informs you to take this action, but if the merge completes with
138+
no conflicts, you may not even notice the message. When there are conflicts, this step should
139+
be completed after fixing the conflicts but before finishing the merge. If there were no conflicts
140+
and the merge completed, you can avoid creating an additional commit by amending the merge commit:
141+
142+
```sh
143+
$ composer update --lock
144+
$ git add composer.lock
145+
$ git commit --amend
146+
```
147+
148+
## Additional Information
149+
150+
### Requirements
151+
152+
The merge driver is written in PHP and requires at least version 5.4.
153+
154+
### Known Limitations
155+
156+
- The merge driver parses and regenerates the JSON to complete the merge. As such, some formatting
157+
may be lost or altered in the process. For the `composer.json` file, the driver attempts to detect
158+
the indentation preference of the file in the working tree and replicate that indentation when
159+
the JSON is regenerated, which should minimize formatting changes. However, some whitespace style
160+
cannot be preserved, e.g. fewer line breaks or more whitespace. While the interpreted contents
161+
of the file will not be compromised, the formatting could result in extraneous changes to the file
162+
as a result of the merge. (In keeping with the behavior of composer, the `composer.lock` file is
163+
regenerated by the default behavior of PHP's `json_encode` function and is consistently indented.)
164+
165+
- When a merge conflict occurs where the last property of an object is removed, accepting the removed
166+
version will result in invalid syntax due to a trailing comma on the preceding property. Care should
167+
be taken to verify the syntax of the overall file and manually update accordingly.
168+
169+
### Acknowledgements
170+
171+
Thanks to [@christian-blades-cb](https://gist.github.com/christian-blades-cb/f75ec813f15393498b6c)
172+
and [@jphass](https://gist.github.com/jphaas/ad7823b3469aac112a52) for the inspiration and examples.

0 commit comments

Comments
 (0)