11

私はブランチにaいます。bブランチをブランチにマージしたいc。マージは早送りではありませんが、手動で解決する必要もありません。(つまり、これは最も単純なケースではありませんが、最も難しいケースでもないため、Gitが人間を必要とせずに独自に実行できるマージです。)

ブランチをチェックアウトせずに、このマージをからbに行う方法はありますか?cどのように?

更新:それを実行できる代替のGit実装を知っている場合は、それも有効なソリューションになります。ただし、プログラムでチェックアウトを実行するスクリプトを作成することは、クリーンな作業ディレクトリが必要になるため、適切な解決策にはなりません。

4

5 に答える 5

8

マージに両方のブランチに触れたファイルが含まれていない場合は、サイドバンド GIT_INDEX_FILEが必要だgit read-treeと思います。git write-treeこれはそれを行う必要があります:

#!/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 -i@ 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扱いbc結果のマージで、いずれかの欠落ファイルを削除として扱うのではなく、それぞれのファイルを結合することもできます。

マージは早送りではないと言うので、read-treeドキュメントを読んで上記のシーケンスを正確に実行する必要があります。 --aggressiveそれは正しいように見えますが、ブランチ間の実際の違いに依存します。

editは、無関係なツリーを処理するために空のツリー ベースを追加しました edit 2

于 2012-05-22T16:22:42.087 に答える
5

作業ディレクトリをクリーンアップする必要がないという要件を考えると、スクリプトを使用しても、作業ツリーまたはインデックスをクリーンアップする必要がないことを意味していると思います。その場合、現在のローカル リポジトリの範囲内で解決策を見つけることはできません。Git は、マージ時にインデックスを広範囲に使用します。競合がなければ作業ツリーについてはわかりませんが、一般的に、マージは現在チェックアウトされているブランチと密接に結びついています。

ただし、現在のレポを何も変更する必要がない別の方法があります。ただし、リポジトリのクローンを持っているか作成する必要があります。基本的には、レポを複製してから、クローンでマージを行い、元のレポにプッシュして戻します。これがどのように機能するかの簡単な例です。

まず、使用するサンプル リポジトリが必要です。次の一連のコマンドで作成されます。最終的にmaster、現在のブランチと、および という名前の変更をマージする準備ができている他の 2 つのブランチになり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-barchange-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

次のシーケンスは、現在のマスター ブランチに干渉することなくマージを完了します。これをスクリプトに詰め込むと、素晴らしい「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

質問?

于 2012-05-19T19:08:14.323 に答える
3

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 "$@" >&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 ${reference@Q} ..."
    git -C "$worktree" merge "$@"
}

prepare_worktree "$1" &&
do_merge "$@" &&
true
于 2018-12-20T21:24:08.563 に答える
1

作業ディレクトリが汚れていても、スクリプトを作成できます。最初に変更を隠しておく必要があります。

git stash
git checkout c
git merge b
git checkout a
git stash pop
于 2012-05-19T17:25:25.580 に答える
-1

この回答では、試すことができる回避策について説明しています。

いいえ、ありません。ターゲット ブランチのチェックアウトは、特に競合を解決できるようにするために必要です (Git が競合を自動的にマージできない場合)。

ただし、マージが早送りになるものである場合は、実際には何もマージする必要がないため、ターゲット ブランチをチェックアウトする必要はありません。ブランチを更新して、新しいヘッド参照。git branch -f でこれを行うことができます:

git branch -f branch-b branch-a
ブランチ a の先頭を指すようにブランチ b を更新します。

于 2012-05-25T02:32:29.353 に答える