Сначала я дам команды, затем, что они на самом деле делают и почему это достигает того, чего вы хотите.Ниже я предполагаю, что ветвь функции просто называется branch
- измените имя, данное на git merge
, если у него есть другое имя.
git status # and make sure everything is committed on your branch
# do any extra commits here if required
git checkout master # change the current branch/commit, index, and work-tree
git merge --squash --no-commit branch
(В git merge
, --squash
означает --no-commit
в любом случае, вы можете опустить второй аргумент флага здесь. Я включил его больше для иллюстрации. Более того, --squash
означает , выполняющее действие слияния в индексе и рабочем дереве, но не делающее коммит слияния.)
Вы можете, в зависимости от ваших инструментов, сделать git reset
(по умолчанию --mixed
reset с HEAD
в качестве коммита), чтобы скопировать коммит HEAD
обратно виндекс, так что обычный git diff
показывает вам все, а git status
показывает все как , не подготовленный для фиксации .
Long
Важно отметить, что здесь, что на самом деле не существует такой вещи, как ожидающих изменений .Git вообще не хранит изменений .Git хранит коммитов , и каждый коммит представляет собой полный снимок всех файлов.Когда вы спрашиваете Git , что изменилось , вы должны сообщить Git о двух коммитах или, по крайней мере, двух деревьях, полных файлов.Затем из этих двух входов Git выясняет, что отличается в снимках A и B. Затем Git сообщает вам об этих различиях, но A и B остаются полными снимками.В некотором смысле ничего из этого не имеет значения - вы видите изменения, когда хотите, и снимки, когда хотите.Но если ваша ментальная модель соответствует тому, что на самом деле делает Git, у вас будет меньше загадок: ваша собственная работа будет легчеСамый важный способ, которым это важно, - помнить: Чтобы увидеть изменения или различия, вы должны предоставить два входа. Иногда подразумевается один из этих входов.Иногда оба из них подразумеваются!
Это последний случай с простым git diff
или git diff --cached
/ git diff --staged
: Git сравнивает индекс с рабочим деревом (обычный git diff
) или HEAD
фиксация индекса (--staged
или --cached
).Опция, если таковая имеется, определяет два входа.То есть Git берет две из трех вещей из этого списка:
-
HEAD
коммит, который является реальным коммитом, содержащим все файлы в специальном Git-only, замороженном / только для чтенияform; - индекс, который по сути является предложенным следующим коммитом и содержит копию всех файлов, также в форме Git-only, но не совсем замороженной;
- рабочее дерево, в котором ваши файлы находятся в их обычной полезной форме.
Имея две из этих вещей в руках, Git сравнивает их и говорит вам, что отличается.Команда git status
выполняет в рамках своей работы оба из этих двух git diff
с (с всегда установленными определенными параметрами, такими как --name-status
и --find-renames=50%
) и суммирует результаты:выходные данные git diff --staged
представляют собой изменения, подготовленные для фиксации , а выходные данные git diff
представляют собой изменения, не подготовленные для фиксации .
Вы также можете запустить git diff HEAD
вручную.,Это выбирает HEAD
и ваше рабочее дерево в качестве двух входных данных: вы явно назвали один из них, HEAD
, в аргументах, а другой подразумевается.Это сравнивает замороженное содержимое HEAD
с незамерзшим содержимым рабочего формата в нормальном формате и показывает разницу.Опять же, Git сравнивает полные снимки: один второй является живым рабочим деревом, снятым с помощью процесса сравнения.
(Кроме того: то, что вы хотите сделать, на мой взгляд, вероятно, является неправильным способом решения этой проблемы. Зачастую вы можете упростить свою работу, совершая ее рано и часто. Выполнение различий - хорошая идея, но вы можете сделать это из base-commit для tip-commit, т. е. через промежуток коммитов, для оценки общего результата. Затем вы можете использовать интерактивное перебазирование или - мой предпочтительный метод - дополнительные ветки, чтобы реконструировать меньшие коммиты в более разумную серию коммиты. Но многое из этого является личным предпочтением. Похоже, ваш инструмент XCode может работать намного лучше, чем он делает; то, как вы его используете, не неправильно , это просто ужасно ограничивает.)
Как и почему это работает
Я начал писать более длинный пост, но он вышел из-под контроля, поэтому я просто скажу: посмотрите некоторые из моих других ответов StackOverflow о том, как Git делает коммиты из индекса, а не из рабочего дерева, и как эти коммиты обновите имя филиала, к которому прикреплен HEAD
.
Слияние Git само по себе также является довольно большой темой. Тем не менее, мы можем сказать, что слияние использует index для достижения результата слияния, при этом work-tree играет второстепенную роль, главным образом для случая, когда возникают конфликты слияния.
Реальное слияние, то есть git merge
, которое выполняет полное трехстороннее слияние и, в конце концов, сделает новый коммит типа merge commit - сделает свой новый коммит с помощью двое родителей. Эти два родителя являются двумя из трех входов, которые были использованы для вычисления результата слияния. Третий вход подразумевается двумя другими; это база слияния . Git находит базу слияния самостоятельно, используя граф коммитов. Затем Git сравнивает базу слияния с подсказками ветвей --ours
и --theirs
, как будто использует две команды git diff --find-renames
: одну от базы к нашей, одну от базы к их.
Игнорирование таких проблем, как добавление, удаление или переименование файлов, сравнение этих трех коммитов дает список тех, кто изменил какие файлы. Если мы изменили какой-то файл, который они не изменили, или они изменили какой-то файл, который мы не изменили, объединить их легко: Git может просто взять наш файл или их файл , Если никто из нас не изменил файл вообще, Git может взять любую из трех версий файла (объединить базу, нашу или их).
Для сложного случая - мы оба изменили какой-то файл - Git для начала использует базу слияния, а объединяет наши изменения и их изменения и применяет объединенные изменения к копии базы слияния. Если наши изменения и их изменения касаются разных строк , объединение будет простым, и Git объявляет результат успешным (даже если в действительности это не имеет смысла). Если мы и они оба изменили одинаковые строки, Git объявит конфликт слияния.
Случай конфликта слияния является самым запутанным. Здесь Git оставляет все три входных файла в индексе и записывает конфликтующее слияние в рабочее дерево. Ваша задача, как человека, бегущего git merge
, состоит в том, чтобы придумать правильное слияние, как вам угодно. Вы можете использовать три индексные копии файла, или копию рабочего дерева, или все эти. Вы решаете слияние самостоятельно и затем используете git add
, чтобы записать правильный результат в указатель, заменяя три не объединенные копии одной объединенной копией. Это решает этот конфликт; После разрешения всех конфликтов вы можете завершить слияние.
Если вы запустите git merge
с --no-commit
, Git выполнит как можно больше слияний - которые могут быть все - и затем остановится, так же, как и для конфликта слияний. Если не было никаких конфликтов, это оставляет и индекс, и рабочее дерево в состоянии готовности к фиксации. (Если были конфликты, опция --no-commit
не имеет никакого эффекта: Git уже собирался остановиться, оставив беспорядок и оставив его, оставив беспорядок для вас.)
Во всеходнако в этих случаях Git оставляет файл в каталоге .git
, сообщая Git, что next commit - будь то из git merge --continue
или git commit
- должно иметь двух родителей , Это делает новый коммит merge commit , который свяжет две ветви:
I--J [master pointed to J when we started]
/ \
...--F--G--H M <-- master (HEAD)
\ /
K--L <-- branch
Это не то, что вы хотите. Итак, мы используем git merge --squash
, который говорит Git: Делайте слияние как обычно, но вместо того, чтобы делать слияние коммит, организуйте следующий коммит как обычный commit. То есть, если бы Git сделал новый коммит M
, он бы выглядел так:
I--J--M <-- master (HEAD)
/
...--F--G--H
\
K--L <-- branch
Без особой уважительной причины, --squash
запрещает коммит, даже если слияние проходит хорошо. Таким образом, после git merge --squash
индекс и рабочее дерево обновляются, но вы можете внести дополнительные изменения в рабочее дерево, использовать git add
, чтобы скопировать обновленные файлы в индекс, и только после этого используйте * 1162. * сделать новый коммит.
(Для более тривиального слияния, когда Git по умолчанию выполняет ускоренную перемотку вперед, --squash
также запрещает режим ускоренной перемотки вперед, как если бы это было git merge --no-ff
. Так --squash
, без --no-ff
и без --no-commit
, достаточно.)
Финальный git reset
, если он используется и необходим, просто копирует коммит HEAD
обратно в индекс. То есть вы, вероятно, хотите, чтобы ваш инструмент сравнивал коммит HEAD
с рабочим деревом, как если бы вы запускали git diff HEAD
. Если инструмент вместо этого сравнивает индекс с рабочим деревом, тот же эффект можно получить, де-обновив индекс до соответствия HEAD
, после того как операция --squash
обновила его.