Простий інструмент "прийняти своє" або "прийняти моє" на весь файл за допомогою git


399

Я не хочу інструменту візуального злиття, а також не потрібно переглядати конфліктуючий файл і вручну вибирати між HEAD (моя) та імпортованою зміною (їх). Більшу частину часу я або хочу всіх їх змін, або всіх моїх. Часто це відбувається тому, що моя зміна зробила його кращим і повертається до мене через тягу, але може бути дещо зміненою в різних місцях.

Чи є інструмент командного рядка, який позбудеться маркерів конфлікту і вибере все так чи інакше на основі мого вибору? Або набір команд git, які я можу зробити собі псевдонімом для виконання кожної з них.

# accept mine
alias am="some_sequence;of;commands"
alias at="some_other_sequence;of;commands"

Робити це досить прикро. Для "прийняти моє" я спробував:

randy@sabotage ~/linus $ git merge test-branch
Auto-merging Makefile
CONFLICT (content): Merge conflict in Makefile
Automatic merge failed; fix conflicts and then commit the result.

randy@sabotage ~/linus $ git checkout Makefile 
error: path 'Makefile' is unmerged

andy@sabotage ~/linus $ git reset --hard HEAD Makefile 
fatal: Cannot do hard reset with paths.

Як я повинен позбутися цих маркерів змін?

Я можу зробити:

git reset HEAD Makefile; rm Makefile; git checkout Makefile

Але це здається досить крутим, має бути кращий шлях. І в цей момент я не впевнений, чи git навіть думає, що злиття відбулося, тому я не думаю, що це обов'язково працює навіть.

Йти іншим шляхом, робити "прийняти своє", так само брудно. Єдиний спосіб я це зрозуміти:

git show test-branch:Makefile > Makefile; git add Makefile;

Це також дає мені переплутане повідомлення про вчинення, яке містить в ньому два рази конфлікти: Makefile.

Чи може хтось, будь ласка, вказати, як зробити вищезазначені дві дії більш простим способом? Дякую


4
Я маю дати це вам як трирічний + командний рядок git, мені здається, що це смішно важко зробити з пам'яті. Це дійсно має бути вбудовано за замовчуванням.
Мавіс Ледфорд

Відповіді:


602

Рішення дуже просте. git checkout <filename>намагається перевірити файл з індексу , тому не працює при злитті.

Що вам потрібно зробити (тобто оформити комісію ):

Щоб оформити власну версію, ви можете скористатись однією з таких функцій:

git checkout HEAD -- <filename>

або

git checkout --ours -- <filename>

або

git show :2:<filename> > <filename> # (stage 2 is ours)

Щоб оформити іншу версію, ви можете скористатись однією з:

git checkout test-branch -- <filename>

або

git checkout --theirs -- <filename>

або

git show :3:<filename> > <filename> # (stage 3 is theirs)

Вам також потрібно запустити "додати", щоб позначити його як вирішене:

git add <filename>

31
Я знайшов , що це трохи дивно , що --oursі --theirsозначає точно протилежне тому , що я інтуїтивно думав, намагаючись цю команду ...
Джошуа Muheim

6
Будьте обережні при використанні git show- це пропускає нормалізацію нового рядка.
Хронічний

2
Це приємно для кількох файлів, але коли у вас багато файлів у конфлікті (оскільки дата в коментарі була змінена!), Як це зробити?
JhovaniC

4
@Santhos: the --Git використовується для відокремлення версій (імен гілок тощо) від імен шляхів (назви файлів, каталогів). Важливо, якщо Git не може вирішити, чи є ім'ям гілки чи назвою файлу. Це слідує умові POSIX (або GNU) використання подвійного тире для відокремлення параметрів від аргументів (назви файлів).
Якуб Нарбський

3
@Sammaron @Joshua Muheim; theirs/ oursМоже виявитися місцями , якщо ви дозвіл конфліктів в контексті операції перебазування. Оскільки перезавантаження працює, перевіряючи цільову гілку, то вибір вишні здійснює з "вашої" гілки на цільову, вхідна зміна ("їхня") відбувається з "вашої" гілки, а поточна гілка - цільова гілка ("наша") ).
RJFalconer

93

Спробуйте це:

Щоб прийняти їх зміни: git merge --strategy-option theirs

Щоб прийняти своє: git merge --strategy-option ours


5
Зауважте, що це дозволить зберегти ваші зміни для ВСІХ конфліктуючих файлів, тому це може бути небезпечно у разі несподіваного конфлікту.
Джон

3
І ви можете використовувати це для інших команд merge-y, таких як cherry-pick та rebase.
idbrii

50

На основі відповіді Якуба ви можете налаштувати такі псевдоніми git для зручності:

accept-ours = "!f() { git checkout --ours -- \"${@:-.}\"; git add -u \"${@:-.}\"; }; f"
accept-theirs = "!f() { git checkout --theirs -- \"${@:-.}\"; git add -u \"${@:-.}\"; }; f"

Вони необов'язково беруть один або кілька шляхів файлів для вирішення та за замовчуванням для вирішення всього, що знаходиться в поточному каталозі, якщо жоден не вказаний.

Додайте їх до [alias]розділу вашого ~/.gitconfigабо запустіть

git config --global alias.accept-ours '!f() { git checkout --ours -- "${@:-.}"; git add -u "${@:-.}"; }; f'
git config --global alias.accept-theirs '!f() { git checkout --theirs -- "${@:-.}"; git add -u "${@:-.}"; }; f'

1
Не працюють для мене ... Це для баша чи іншої оболонки?
користувач456584

Це псевдоніми git, додайте їх до [alias]розділу у вашому ~.gitconfigабо використанні git config --global accept-ours "...". Відредагували мою відповідь.
kynan

2
Ви не знаєте, скільки часу цей псевдонім врятував мене. Пальці вгору!
Адам Паркін

1
@hakre Переконайтесь, що ви цитуєте псевдонім, інакше ваша оболонка спробує інтерпретувати його. Або просто вручну відредагуйте свій ~/.gitconfig.
kynan

1
Синтаксис оболонки для значень за замовчуванням:!f() { git checkout --ours -- "${@:-.}" git add -u "${@:-.}; }; f
jthill

17

Виходячи з відповіді Кінана, тут є ті самі псевдоніми, модифіковані, щоб вони могли обробляти пробіли та початкові тире у назви файлів:

accept-ours = "!f() { [ -z \"$@\" ] && set - '.'; git checkout --ours -- \"$@\"; git add -u -- \"$@\"; }; f"
accept-theirs = "!f() { [ -z \"$@\" ] && set - '.'; git checkout --theirs -- \"$@\"; git add -u -- \"$@\"; }; f"

0

Ідеальна ситуація для вирішення конфліктів - це коли ви заздалегідь знаєте, яким чином ви хочете їх вирішити, і можете передати -Xoursабо -Xtheirsрекурсивні варіанти стратегії злиття. Поза цим я бачу три сценарії:

  1. Ви хочете просто зберегти єдину версію файлу (це, мабуть, слід використовувати лише у незмінюваних бінарних файлах, оскільки в іншому випадку конфліктні та неконфліктні файли можуть вийти із синхронізації один з одним).
  2. Ви хочете просто вирішити всі конфлікти в певному напрямку.
  3. Ви повинні вирішити деякі конфлікти вручну, а потім вирішити всі інші в певному напрямку.

Щоб вирішити ці три сценарії, ви можете додати у .gitconfigфайл (або еквівалент) наступні рядки :

[merge]
  conflictstyle = diff3
[mergetool.getours]
  cmd = git-checkout --ours ${MERGED}
  trustExitCode = true
[mergetool.mergeours]
  cmd = git-merge-file --ours ${LOCAL} ${BASE} ${REMOTE} -p > ${MERGED}
  trustExitCode = true
[mergetool.keepours]
  cmd = sed -I '' -e '/^<<<<<<</d' -e '/^|||||||/,/^>>>>>>>/d' ${MERGED}
  trustExitCode = true
[mergetool.gettheirs]
  cmd = git-checkout --theirs ${MERGED}
  trustExitCode = true
[mergetool.mergetheirs]
  cmd = git-merge-file --theirs ${LOCAL} ${BASE} ${REMOTE} -p > ${MERGED}
  trustExitCode = true
[mergetool.keeptheirs]
  cmd = sed -I '' -e '/^<<<<<<</,/^=======/d' -e '/^>>>>>>>/d' ${MERGED}
  trustExitCode = true

get(ours|theirs)Інструмент просто зберігає відповідну версію файлу і відкидає всі зміни від іншої версії (так не злиття не відбувається).

merge(ours|theirs)Інструмент повторно виконує три способи злиття з місцевою, бази і віддалені версії файлу, вибираючи для вирішення конфліктів в даному напрямку. Це має певні застереження: він ігнорує різні варіанти, передані команді злиття (такі як алгоритм та обробка пробілів); чи є злиття чисто з оригінальних файлів (тому будь-які вручні зміни до файлу відкидаються, що може бути добре чи погано); і має перевагу в тому, що його не можна переплутати маркерами розбіжності, які повинні бути у файлі.

keep(ours|theirs)Інструмент просто редагує з маркерів Diff і закриті секції, їх виявлення з допомогою регулярного виразу. Перевага полягає в тому, що він зберігає варіанти відмінностей від команди злиття і дозволяє вирішити деякі конфлікти вручну, а потім автоматично вирішити решту. Недоліком є ​​те, що якщо у файлі є інші маркери конфлікту, він може заплутатися.

Всі вони використовуються при запуску, git mergetool -t (get|merge|keep)(ours|theirs) [<filename>]де, якщо <filename>не надається, обробляє всі конфліктуючі файли.

Взагалі кажучи, якщо ви знаєте, що немає відмінних маркерів, які б переплутали регулярний вираз, keep*варіанти команди є найпотужнішими. Якщо ви залишите mergetool.keepBackupопцію не встановленою або істинною, то після злиття ви можете відрізнити *.origфайл від результату злиття, щоб перевірити, чи є він сенс. Як приклад, я запускаю наступне після того, як mergetoolпросто перевірити зміни перед вчиненням:

for f in `find . -name '*.orig'`; do vimdiff $f ${f%.orig}; done

Примітка : Якщо merge.conflictstyleце не так, потрібно замість diff3цього використовувати /^|||||||/шаблон у sedправилі /^=======/.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.