问题
I use a couple of software packages (like gitlab) that you install by cloning from their git repo. They typically come with some config.example (under version control), which you copy to your own config file (not under version control or even ignored in .gitignore) and adapt to your needs.
When the upstream package is updated and for example changes the config file options that will obviously only be reflected in config.example.
Is there a chain of git commands that i'm missing that can help me compare the changes of config.example to the new one in upstream/HEAD and maybe even merge them interactively into my local config file?
Would be awesome if i could get something like the interactive patch mode in git add/commit --interactive.
回答1:
git checkout --patch selects diff hunks, simplest here might be to put your content at the upstream path, do that, and clean up after:
cp config config.example
git checkout -p upstream config.example
mv config.example config
git checkout @ config.example
that will get you the two-diff hunk selection from git add --patch.
回答2:
- You can use
vimas a merge tool withvimdiff. Emacscan do this as well withediff-mode.
回答3:
You could probably approach this in many ways. I could think of two: git-merge-file and good old patch. The git-merge-file method offers some interactivity, whereas the patch method offers none.
Solution with git-merge-file
Lets say you have an original file config.example which you use to create a local unversioned file, config.local. Now when upstream updates config.example, you can follow something like the following steps to merge any new changes.
$ git fetch
$ git show master:config.example > config.example.base
$ git show origin/master:config.example > config.example.latest
$ git merge-file config.local config.example.base config.example.latest
This will update config.local with the usual conflict markers, which you will then have to resolve with your favourite merge tool (ediff in Emacs is nice, I'm sure there are similar modes for Vim). For example, the following three files,
config.example.base:
Original:
some config
config.example.latest:
Original:
some config
Upstream:
new upstream config
config.local:
Original:
some config
My changes:
some other config
will be merged like this:
Original:
some config
<<<<<<< config.local
My changes:
some other config
=======
Upstream:
new upstream config
>>>>>>> config.example.latest
You could probably script this without much effort. Btw, git-merge-file can operate on any 3 files, they need not be version controlled under git. That means one could use it to merge any three files!
Solution with patch:
Assuming the same file names, config.local and config.example, the following should work.
$ git fetch
$ git diff master..origin/master -- config.example | sed -e 's%\(^[-+]\{3\}\) .\+%\1 config.local%g' > /tmp/mypatch
$ patch < /tmp/mypatch
回答4:
If you want the full git merge, you can get git to do it with arbitrary content by setting up an index entry as git read-tree does and then invoking git's normal merge driver on just that entry. Git refers to the different content versions as "stages"; they are 1: the original, 2: yours, 3: theirs. The merge compares the changes from 1 to 2 and from 1 to 3 and does its thing. To set it up, use git update-index:
orig_example= # fill in the commit with the config.example you based yours on
new_upstream= # fill in the name of the upstream branch
( while read; do printf "%s %s %s\t%s\n" $REPLY; done \
| git update-index --index-info ) <<EOD
100644 $(git rev-parse $orig_example:config.example) 1 config
100644 $(git hash-object -w config) 2 config
100644 $(git rev-parse $new_upstream:config.example) 3 config
EOD
and you've staged a merge of custom content for that path. Now do it:
git merge-index git-merge-one-file -- config
and it'll either automerge or leave the usual conflict droppings, fix it up as you like and git rm --cached --ignore-unmatch (or keep) the index entry if you want.
The pathname you put in the index (the "config" in all three entries here), by the way, doesn't have to already exist or have anything to do with anything. You could name it "wip" or "deleteme" or anything you want. The merge is of the content id'd in the index entry.
I think that's likely to do what you want here. If you really do want to pick and choose from the upstream changes you can put your own content at config.example and do git checkout -p upstream -- config.example, that does the inverse of git add -p, then put things back the way they were.
回答5:
To diff just config.example in your local repo with the corresponding file in upstream/HEAD you can run:
git diff upstream/HEAD config.example
Unfortunately I don't know of a way to make git directly apply the changes to a file that git doesn't track.
回答6:
There is a tool called sdiff that might do what you want.
Invoke it (in your case) with sdiff -o config config.example config
回答7:
The following should work:
git diff <some-args> | perl -pe 's/path\/to\/changes\/file/path\/other/g' > t
patch -p1 < t
rm t
来源:https://stackoverflow.com/questions/30413231/interactively-merge-files-tracked-with-git-and-untracked-local-files