75

I want to take advantage of the git-merge algorithm on two arbitrary files in a git repo. Here is my working directory:

folder/
    file1
    file2

file1 and file2 are similar, but I want to see how git would merge them as if they were different versions of the same file. In other words, I want something like this:

git merge-files file1 file2 > merge_of_file1_file2

Is there a way to do this?

Alexander Bird
  • 38,679
  • 42
  • 124
  • 159

4 Answers4

71

This doesn't really make sense, because you're not providing a common ancestor. If you do have one, however, you can use:

git merge-file <current-version> <common-ancestor> <other-version>

This places the results in the current version file; if you want them elsewhere, use:

git merge-file -p <current> <common> <other> > <dest>

It needs the common ancestor to provide something to consider changes relative to. You could hack it, by providing an empty file or a copy of an older version of one of them from the history of your repository, but the quality of results will depend on how well you select a common ancestor, since it's merging the two diffs, between that and each of the new versions. An empty file will only work well if the two are very similar (many runs of at least three identical lines).

Without that, all you can really do is look at the differences:

git diff --no-index file1 file2
Cascabel
  • 479,068
  • 72
  • 370
  • 318
  • 6
    What about using a copy of one of the files as the common ancestor? – Carl G Feb 13 '13 at 14:27
  • 5
    @CarlG, Let's say file2 contains one more line than file1. If you choose file1 as the ancestor, this line is interpreted as an addition so the merge will be file 2. If you choose file2 as the anchestor, the line is interpreted as a deletion and you get file1 as a result. – Carlo Roosen Sep 05 '13 at 07:07
  • 2
    @CarlG To put it another way, if you start out with A and one side changes it to B while the other side keeps it as A, the merged result is clearly B. – Cascabel Sep 05 '13 at 07:11
  • what should be `common-ancestor` an empty file? – alper Oct 24 '20 at 13:57
  • Maybe it would work well if one of the files would be the ancestor? – radrow Feb 28 '21 at 19:54
59

For what you are trying to do:

touch file3  #empty
git merge-file file1 file3 file2

This will do a three-way merge of file1 and file2 with file3 ( empty file ) as the base.

Note that this writes to file1. You can of course do the below if that is not desired:

touch file3
cp file1 merge_of_file1_file2
git merge-file merge_of_file1_file2 file3 file2
manojlds
  • 290,304
  • 63
  • 469
  • 417
  • 4
    I verified that this works (via Git Bash). Here is the bash function I wrote to encapsulate this into a single command: ``` function merge() { touch __merge_result__ git merge-file $1 __merge_result__ $2 rm __merge_result__ } ``` – May Oakes Dec 08 '15 at 23:13
  • 2
    This shows the complete file as changed instead of the changed lines – alper Oct 24 '20 at 13:50
5

Just to add on to manojlds's answer, here's a nice, complete function you can add to your .bashrc that does the job. It has the benefit of properly identifying your two files' names in the "merge conflict" blocks.

merge() {
  local ext
  [ $# -ne 2 ] && echo "Error: Need exactly two args." && return 1
  [[ ! -r $1 || ! -r $2 ]] && echo "Error: One of the files is not readable." && return 1
  if [[ ${1##*/} =~ '.' || ${2##*/} =~ '.' ]]; then
    [ ${1##*.} != ${2##*.} ] && echo "Error: Files must have same extension." && return 1
     ext=.${1##*.}
  fi
  touch tmp$ext # use empty file as the 'root' of the merge
  cp $1 backup$ext
  git merge-file $1 tmp$ext $2 # will write to file 1
  mv $1 merge$ext
  mv backup$ext $1
  rm tmp$ext
  echo "Files merged into \"merge$ext\"."
}
Luke Davis
  • 2,548
  • 2
  • 21
  • 43
1

You can also use KDiff3 for this.

  • Select file one
  • Select file two

And merge :)

http://kdiff3.sourceforge.net/

dominic
  • 367
  • 2
  • 5