Как выполнить непереходное переключение git к ветке, которая не удалена?

Я на ветке a. Я хочу объединить ветвь b в ветвь c. Слияние не является быстрой перемоткой вперед, но также не требует ручного разрешения. (т.е. это не простейший случай, но он также не самый сложный, поэтому это слияние, которое Git может делать самостоятельно, не нуждаясь в человеке.)

Есть ли способ сделать это слияние от b до c без необходимости проверять какую-либо ветвь? Как?

ОБНОВЛЕНИЕ: Если вы знаете альтернативную реализацию Git, которая может это сделать, это также будет правильным решением. Но написать script, который будет делать программные проверки программным способом, не будет хорошим решением, потому что он все равно потребует, чтобы у меня был чистый рабочий каталог.

Ответ 1

Если слияние не связано с файлами, затронутыми в обеих ветвях, я думаю, что вы хотите git read-tree и git write-tree с боковой полосой GIT_INDEX_FILE. Это должно сделать это:

#!/bin/sh
export GIT_INDEX_FILE=.git/aux-merge-index
trap 'rm -f '"'$GIT_INDEX_FILE'" 0 1 2 3 15
set -e
git read-tree -im `git merge-base $2 $1` $2 $1
git write-tree \
| xargs [email protected] git commit-tree @ -p $2 -p $1 -m "Merge $1 into $2" \
| xargs git update-ref -m"Merge $1 into $2" refs/heads/$2

Вы также можете использовать git mktree </dev/null вместо merge-base для обработки b и c как полностью не связанных между собой ветвей и сделать полученное в результате объединение объединять файлы в каждом, а не обрабатывать файлы, отсутствующие в удалении.

Вы говорите, что ваше слияние - это не ускоренная перемотка вперед, поэтому вам нужно будет прочитать документы read-tree, чтобы сделать последовательность выше, сделайте именно то, что вы хотите. --aggressive выглядит так, как будто это правильно, это зависит от того, каковы фактические различия между вашими ветвями.

edit добавлена ​​база с пустым деревом для обработки несвязанных деревьев изменить 2 поднять какую-то полезную нагрузку Я сказал или оставил неявным в комментарии

Ответ 2

Учитывая ваше требование не очищать рабочий каталог, я предполагаю, что вы имеете в виду, что вы не хотите очищать свое рабочее дерево или индекс даже через некоторые сценарии. В этом случае вы не найдете решения в рамках вашего текущего локального репо. Git сильно использует индекс при слиянии. Я не уверен в рабочем дереве, если конфликтов нет, но в общем случае слияние неразрывно связано с ветвью в настоящее время.

Однако другой способ, который не требует, чтобы вы что-либо изменяли в своем текущем репо. Тем не менее, он требует, чтобы вы создали или создали клон своего репо. В основном, просто клонируйте свое репо, затем выполните слияние с клоном и верните его обратно в исходное репо. Вот краткий пример того, как это будет работать.

Во-первых, нам нужен образец репо для работы. Следующая последовательность команд создаст ее. Вы получите master как текущую ветвь и две другие ветки с изменениями, готовыми к объединению с именами change-foo и change-bar.

mkdir background-merge-example
cd background-merge-example
git init
echo 'from master' > foo
echo 'from master' > bar
git add .
git commit -m "add foo and bar in master" 
git checkout -b change-foo
echo 'from foo branch' >> foo
git commit -am "update foo in foo branch"
git checkout -b change-bar master
echo 'from bar branch' >> bar
git commit -am "update bar in bar branch"
git checkout master

Теперь представьте, что вы работаете над master, и вы хотите объединить change-bar в change-foo. Здесь полуграфическое изображение того, где мы находимся:

$ git log --oneline --graph --all
* c60fd41 update bar in bar branch
| * e007aff update foo in foo branch
|/  
* 77484e1 add foo and bar in master

Следующая последовательность выполнит слияние без вмешательства в текущую ведущую ветвь. Упакуйте это в script, и у вас есть хорошая команда "background-merge":

# clone with absolute instead of relative path, or the remote in the clone will
# be wrong
git clone file://`realpath .` tmp
cd tmp
# this checkout auto-creates a remote-tracking branch in newer versions of git
# older versions will have to do it manually
git checkout change-foo
# creating a tracking branch for the other remote branch is optional
# it just makes the commit message look nicer
git branch --track change-bar origin/change-bar
git merge change-bar
git push origin change-foo
cd ..
rm -rf tmp

Вкратце, это приведет к клонированию текущего репо в подкаталог, введите этот каталог, выполните слияние, а затем верните его обратно в исходное репо. Он удаляет подкаталог после его завершения. В большом проекте вы можете захотеть иметь специальный клон, который просто обновлялся, вместо того чтобы делать новый клон каждый раз. После слияния и нажатия мы заканчиваем:

$ git log --oneline --graph --all
*   24f1916 Merge branch 'change-bar' into change-foo
|\  
| * d7375ac update bar in bar branch
* | fed4757 update foo in foo branch
|/  
* 6880cd8 add foo and bar in master

Вопросы?

Ответ 3

Вы можете написать script, даже если ваш рабочий каталог загрязнен. Сначала вам нужно сначала внести изменения.

git stash
git checkout c
git merge b
git checkout a
git stash pop

Ответ 4

В дополнение к очень сложному ответу gj git read-tree @jthill, есть (в настоящее время) ИМХО более простой способ сделать это с использованием git worktree. Это основной подход:

$ git worktree add /tmp/wt c
$ git -C /tmp/wt merge b
$ git worktree remove /tmp/wt

Ниже приведен небольшой скрипт Bash, который вы можете назвать так:

$ worktree-merge c b

Скрипт worktree-merge:

#!/usr/bin/env bash

log() {
    echo -n "LOG: "
    echo "[email protected]" >&2
}

escape() {
    local string="$1"
    echo "${string//[. \/]/-}"
}

cleanup() {
    trap "" SIGINT
    log "Removing temporary worktree '$1' ..."
    git worktree remove --force "$1"
}

prepare_worktree() {
    local reference="$1"
    local worktree="/tmp/MERGE-INTO-'escape "$reference"'"

    log "Creating temporary worktree '$worktree' ..."
    trap "cleanup $worktree" EXIT
    git worktree add --force "$worktree" "$reference"
}

do_merge() {
    local reference="$1"
    local worktree="/tmp/MERGE-INTO-'escape "$reference"'"
    shift

    log "Merging ${@@Q} into ${[email protected]} ..."
    git -C "$worktree" merge "[email protected]"
}

prepare_worktree "$1" &&
do_merge "[email protected]" &&
true

Ответ 5

Этот ответ объясняет обходное решение, которое вы можете попробовать.

Нет, нет. Для устранения конфликтов, в частности, требуется проверка целевой ветки (если Git не может автоматически объединить их).

Однако, если слияние - это ускоренная перемотка вперед, вам не нужно проверять целевую ветку, потому что на самом деле вам не нужно ничего слить - все, что вам нужно сделать, это обновить ветвь до указывают на новый заголовок ref. Вы можете сделать это с помощью Git branch -f:

git branch -f branch-b branch-a
Обновит ветку-b, чтобы указать на ветку ветки-a.