web-dev-qa-db-ja.com

Subversionのリベース?

この方法でブランチをマージしやすくなり、競合が少なくなります。

トランクを新しいブランチにコピーし、機能ブランチとマージします。完了したら、新しいブランチをトランクにマージします。この手法は、Mercurialとgitのリベースによく似ています。

以前は、トランクから機能branche/sへの変更をマージしていました。しかし、後で機能ブランチをトランクにマージして戻すと、トランクからのものの一部が再びトランクにマージされ、多くの競合が発生しました。再統合マージの選択がありますが、私にはうまくいかないようでした。

誰もが似たようなSubversionリベースを行いますか?私は最近これを始めたばかりで、副作用を見ていません。これにより、予期しない問題が発生しますか?

40
bo bo

一般的に、リベースとは、機能ブランチを上流ブランチにマージする前に、上流ブランチの変更を機能ブランチに組み込む行為です。

Gitでは、ブランチが作成されてから行われた変更が最初に取り外されてバッファリングされ、アップストリームの変更が適用され、次にバッファされた変更が適用されるため、プロセスはさらに洗練されます。ここでの要点は、トランクを機能ブランチにマージすることであり、gitの用語ではリベースではありません。 gitアプローチには多くの利点がありますが、すべてのコミットをサーバーに保存する必要があるため(svnは配布されないため)、svnでは非常にきれいに実装できませんが、svnではcanを実行します。

'svn rebase'(gitの方法)は次のようになります

  1. svn cp trunk feature
  2. 機能とトランクへのコミット
  3. svn cp trunk feature-rebase
  4. svn co feature-rebase
  5. cd feature-rebase
  6. svn merge feature
  7. svn commit
  8. svn rm feature
  9. svn mv feature-rebase feature
  10. (feature-rebase WCに戻る)svn switch feature

その後、最終的にトランクの作業コピーで、svn merge --reintegrate feature

トランクを機能ブランチに単純にマージすることとの違いがわかりますか?この例では、上流の最新のトランクから開始し、機能からの変更をその上にマージします。

トランクでのコミットの一部は、別の機能ブランチをトランクにマージすることで発生する可能性があることを想像してください。そのため、直接トランクにコミットすることを主張していません。

32
quickshiftin

SVNでリベースを実現する方法を教えてくれる巧妙なトリックがあればいいのにと思いますが、主にjdehaanが言及している手動でのチェリーピッキングを必要とする複雑さのために、SVNでトランクの変更を伴うブランチの手動更新を常に避けています。

私が代わりに行うことは、通常、ブランチからトランクへの変更をマージし、ブランチを削除してから、トランクからブランチを再作成するというプラクティスに従います。これにより、機能ブランチを更新/リベースできますが、そのブランチからの以前の変更がトランクの一部になるという不幸な副作用があります。このため、機能ブランチが安定して使用可能なポイントにある場合にのみこのプラクティスに従いますが、さらに大きな目的をさらに完了するために、その機能の作業を続けたいと考えています。

私が好むのは、トランクの変更をブランチにマージしてブランチをリフレッシュすると、その後のブランチからの再統合マージがプロセス中にリベースされたリビジョンをプルしないことです。 merge-infoプロパティに基づいてこれを行うことは可能であるはずですが、jdehaanが述べていることと私が恐れていることによると、これを行うにはチェリーピッキングが必要です。

適切なリベースの実装では、別のブランチからブランチが作成される階段の例を考慮することもできることに注意してください。

更新:Subversionドキュメントによると、 -reintegrateを使用するとoption Subversionは、ベースの変更をブランチに取り込むために行われた可能性のある更新マージを考慮して、ブランチで行われた作業を適切に再統合できる必要があります。もちろん、これは技術的にはリベースとは少し異なりますが、リベースと呼ばれるほど使用方法が似ていると思います。

7
jpierson

私の会社では、次のアプローチを使用しています。

  1. 課題トラッカーの各タスクNK- $ Xには、個別のブランチbranch/NK- $ Xがあります。
  2. svn cp trunk branches/NK- $ Xでタスクの作業を開始します
  3. 変更をトランクに直接コミットすることはありません。スケジュールされた更新UPDNK- $ Xごとに、個別のブランチ/ UPDNK- $ Xがあります。更新の直前にsvn cp trunk branches/UPDNK- $ Xで作成します。
  4. 更新UPDNK- $ YのタスクNK- $ Xがスケジュールされると、ブランチ/ NK- $ X inot UPDNK- $ Yがマージされます。それはcd UPDNK- $ Yです。 svn merge -r start:HEADブランチ/ NK- $ X
  5. uPDNK- $ Yの準備ができたら、トランクにマージします。それはcd trunkです; svn merge -r start:HEAD branches/UPDNK- $ Y

タスクNK- $ Xが1反復サイクルより長く続き、更新が必要な場合は、決してトランクをNK- $ Xにマージしないでください。自分で書いたものだけをブランチにコミットするというルールがあり、それによりすべてが簡単になります。代わりに:

cd NK-$X
svn log
//let L = the number of the last changeset to this branch changeset
//let F = the number of the first changeset to this branch
svn rm branches/NK-$X 
svn cp trunk branches/NK-$X 
svn up
svn merge -r F:L branches/NK-$X@L 
svn ci -m 'refereshed'

このように、ブランチ/ NK- $ Xの変更ログを見ると、開発者によって実際に実行された変更のみが表示されます。

更新:上記のワークフローは自動化できるため、githubでプロジェクトを開始しました: svn rebase

4
qbolec

使用する git svn

git svn clone -s <link to svn trunk/branches/tags parent>

git rebaseコマンドを使用してください

0
Sergei

私は、svnのリベースのようなgitを実行するスクリプトを使用します。

#!/bin/bash

set_safe()
{
    set -eo pipefail
}

validate_number()
(
    if (! [ "$1" -eq "$1" ] 2>/dev/null ) then
    {
        echo "$1 is not a number"
        return 1
    }
    fi
)

get_line()
(
    set_safe
    #head -n "$1" | tail -n 1
    sed -n "${1}p;$(($1+1))q"
)

split_trim()
(
    set_safe
    tr "$1" '\n' | sed -e 's/^\s*//;' -e 's/\s*$//'
)

run_svn()
(
    set +x
    #echo "svn $*" 1>&2
    svn --non-interactive --password "$svn_password" "$@"
)

rebase()
(
    set_safe

    url="$1"
    cur="$2"
    end="$3"

    validate_number "$cur"
    if ([ -z "$end" ] || [ "$end" = "HEAD" ]) then
    {
        end="$(run_svn info "$url" | grep "Last Changed Rev" | cut -d' ' -f4)"
        echo "end: $end"
    }
    else
    {
        validate_number "$end";
    }
    fi

    while (true) do
    {
        log="$(run_svn log "$url" -l1 -r "$cur:HEAD")"
        meta="$(echo -n "$log" | get_line 2 | split_trim '|')"
        next="$(echo -n "$meta" | get_line 1 | tail -c +2)"
        author="$(echo -n "$meta" | get_line 2)"
        date="$(echo -n "$meta" | get_line 3 | awk '{print $1, $2, $3}')"
        msg="$(echo -n "$log" | tail -n +4 | head -n -1)"
        cur=$next

        if ([ -z $cur ] || [ $cur -gt $end ]) then { break; } fi

        echo "$msg" > .msg

        echo "Merging revision $cur:"
        echo "========"
        cat .msg
        echo "========"

        run_svn merge "$url" -c $cur
        run_svn commit -F .msg
        rm -f .msg
        run_svn update

        echo "Success"
        echo

        cur=$(($cur + 1))
    }
    done
)

if ([ -z "$1" ]) then
{
    echo "Usage:"
    echo "    svn-rebase.sh <url> <begin revision> [end revision]"
    exit
}
fi

echo -n "svn password: "
read -s svn_password
echo

rebase "$1" "$2" "$3"
err=$?
if ([ $err -ne 0 ]) then { echo "rebase failed: $err"; } fi
exit $err

他のブランチのリビジョンを1つずつマージします。

0
anton_rh

私はこのアプローチを使用しています:

リベースでは、再度マージするときにリベースされたリビジョンを引き継がないように注意する必要があります。マージに関しては、チェリーピッキングを行います。機能ベースの変更セットではなく、何か新しいものを実装するリビジョンのみを選択します。その後、正常に動作するはずです。コメント:何かのために再統合ブランチを使用したことを思い出すことはできません。非常に単純なユースケースのみを対象としていると思います。

新しいアプローチでは、必要に応じてトランクから機能ブランチへのリベースをどのように処理するかは説明から明らかではありません。リベースを完全に禁止しますか? svnでの分岐は安価な操作であるため、これもオプションです。

0
jdehaan