50

私は git を使用していますが、リモート リポジトリと同期されていないコミットを作成できるようにしたいと考えています。このようなコミットは、履歴への影響を避けるために、ローカル リポジトリ内の他のすべてのコミットの上に「浮かぶ」必要があります。このようなコミットを使用して、ローカル固有の変更 (構成の変更、デバッグ フラグ、ローカルの回避策など) を保存できます。

現在、コミット時に手動でリベースしてコミットを先頭に並べ替え、HEAD^ローカルの変更をプッシュしないように使用をプッシュしています。変更をスタッシュに入れることも検討しましたが、スタッシュの通常の使用が妨げられるため、あまり便利ではありません。もう 1 つの方法は、これらすべてのローカル変更をステージングせずにそのままgit add -pにして、コミットするたびに使用することです。ただし、些細なローカル変更が多数あると、面倒になります。

これが私の現在のワークフローの例です。

私のリポジトリは最初は次のようになります

A---B---C---F master

ここで、「F」は私のフローティングコミットです。

私はコミットします:

A---B---C---F---D master

次にgit rebase -i HEAD~2並べ替えます。

A---B---C---D---F master

次に、ローカルコミットgit push remote HEAD~1...以外のすべてをプッシュします。F

変更 F には、既存のバージョン管理されたファイルへの変更が含まれており、任意の量の変更が含まれる場合があります。(複数のコミットを「フローティング」できる場合は、ローカルの変更を分離できるため、さらに優れています)。

4

9 に答える 9

22

これらの変更を、メインの開発ブランチから定期的にリベース/マージするローカル ブランチに入れるのはどうですか? そうすれば、それらを上流にコミットする危険はありません。

于 2012-11-07T19:47:45.473 に答える
18

したがって、次の 2 つのことが必要なようです。

  • 一部のコミットは非公開にして (たとえば、ローカル ブランチ上で)、プル時にプッシュまたはマージしないようにする必要があります。共有コミットの「後」に残す必要があります。

  • これはほとんど透過的です。マスターで作業し、ローカルブランチを自動的に維持したい。どのコミットをローカルにするかを決定するだけで、リモート リポジトリと対話するコマンドはそれらを無視します。

したがって、これらのコミットを識別して処理するスクリプトを作成する必要があります (名前git-somethingを付けてパスに配置するため、追加の git コマンドになります)。スクリプトがローカルコミットを認識するためのトリガーが必要です。これを行う簡単な方法は、スクリプトが認識できるように、コミットの説明 (実際のコミットや共有コミットでは決して使用しないもの) に魔法の言葉を入れることです。(それがあなたにとってあまりにも不安定に聞こえる場合は、コミットのツリーで特別なファイルを使用することもできます.THIS_COMMIT_IS_LOCAL_ONLY.

現在のインデックス/作業ディレクトリからローカル コミットを行うコマンドが必要です。これは簡単で、呼び出すだけですgit commit $@ -m "__LOCAL_COMMIT_ONLY__"(これは一例です。重要なのは、作成中のコミットをローカルのみとしてマークし、git commit を延期することです)。また、すべてのローカル コミットを一時的にポップし、他の git コマンド (pull、push、fetch、merge など) を実行してから、ローカル コミットを再適用するコマンドも必要です。また、このコマンドを使用して、共有する予定のローカル コミットを作成し、履歴のローカルのみのコミットの「下」に常に表示されるようにします。

両方を 1 つにまとめたスクリプトの例を次に示します。

#!/bin/sh
if [[ $1 eq 'new' ]]; then
  shift
  exec git commit $@ -m "__LOCAL_COMMIT_ONLY__"
elif [[ $1 eq

OLD_HEAD=$(git rev-parse HEAD)
OLD_REAL_HEAD="$(git rev-list HEAD --grep=__LOCAL_COMMIT_ONLY__ | tail -n1)^"
git reset --soft $OLD_REAL_HEAD
git $@
git rebase --onto HEAD $OLD_REAL_HEAD $OLD_HEAD

ここで、スクリプトを呼び出すと仮定するとgit-localgit local newインデックスから新しいローカルのみのコミットを作成する (またはgit local new -a作業ディレクトリ内の変更されたファイルからgit local commit作成する) ために使用し (悲しいことに、名前は不完全です)、新しい「実際の」コミットを作成します。git local push押す、git local pull引くなど。

これの主な欠点は、ほとんどのコマンドに接頭辞が付いていることを覚えておく必要があることですlocal。これを一度忘れてしまうと、少しうんざりしますが、それほど悪くはありません.クイックgit rebase -iを使用すると、ローカルコミットを簡単にトップに戻すことができ、オフにして再び実行できます. 最大のリスクは、誤ってgit push代わりに使用git local pushして、すべてのプライベートな変更をアップストリームに送信することです。これは、全員を悩ませます。そのためには、実際に git 自体の代わりに呼び出す小さなラッパー スクリプトを作成することをお勧めします (それを呼び出して、パス上にある~/bin/gitことを確認~/binしてください)。

#!/bin/sh
if [[ $1 = 'push' ]]; then
  if /usr/bin/git rev-list HEAD --grep=__LOCAL_COMMIT_ONLY__ | grep -q .; then
    echo "Can't push with local changes still active!"
    echo "Try using `git local push' instead."
    exit 1
  fi
fi
exec /usr/bin/git "$@"

pre-receiveメッセージに含まれるコミットを自動的に拒否するフックをサーバーに作成することもでき__LOCAL_COMMIT_ONLY__ます。

于 2012-11-18T18:34:08.493 に答える
13

Git から生成できるパッチ ファイルを使用できます。

# git diff > local.patch

コミットするときは、パッチを逆にします。

# git apply -R local.patch

コミット後、パッチを復元します。

# git apply local.patch

パッチ ファイルを作成する前に、ローカルの変更のみが作業ツリーにあることを確認する必要があります。パッチ ファイルを作成したら、.gitignoreコミットしないように追加できます。

于 2012-11-13T19:21:18.613 に答える
12

前の仕事では、誰もが私たちが個々の設定をコミットした独自のローカルsettingsブランチを持っていました。このブランチはに基づいていましたmaster; トピックブランチはから分岐しましたsettings。トピックを統合する準備ができたら、それらをにリベースしましmasterた。これは@koljaTMが示唆していることのようです。

A---B---C  master
         \
          F  settings
           \
            D  topic

rebase --onto master settings topic

A---B---C  master
        |\
        | F  settings
         \
          D  topic

新しい変更がヒットした場合master、をリベースしてsettingsからmaster、作業中のトピックをリベースしますsettings

確かに、これはワンステップの「このフローティングを永久に残す」ソリューションではありませんが、クリーンで十分にシンプルです。

于 2012-11-07T20:01:58.967 に答える
2

コミットする必要がある他の変更を含まないローカル固有の変更がファイルにあると仮定すると、git update-indexを介して、そのファイルの変更を無視するように git に指示できます。特に --assume-unchanged または --skip-worktree のいずれかが必要です。それらは基本的に同じことを行いますが (git index でファイルを未変更としてマークします)、ここで詳しく説明されているいくつかの非常に微妙な特性があります。

于 2013-04-10T12:04:47.903 に答える
1

進行を妨げようとしているのが実際のコミットである場合、正しい軌道に乗っています-使用

git rebase -i

すべての「個人的なコミット」を最新のコミットになるように並べ替えます。

git push <remotename> <latest commit SHA you want to push>:<remotebranchname>

これにより、指定した SHA までのすべてのコミットがプッシュされますが、その後の「個人的なコミット」はプッシュされません。

于 2012-11-17T22:21:21.280 に答える
0

git の特定のファイルを無視して、リモート リポジトリにプッシュされないようにすることができます。私がそのようなことをする方法は、それらのファイルをローカルに保持し、それらを無視してリポジトリにプッシュしないことです。

ここで gitignore について説明します。

https://help.github.com/articles/ignoring-files

于 2012-11-07T19:39:27.393 に答える
0

私は、共有コード ベースから IDE 設定などを分割することで、常にこの問題に遭遇してきました。これは、今では解決されていると思われるほど一般的な使用例ですが、まだ解決されていません。あなたが望むようにこれを処理できるとは思いませgitん。

問題の本質は、2 つのチェックアウトで 1 つのディレクトリを共有する必要があることです。これを機能させるには、ユーザーが操作できるデータ構造が必要で、どのファイルがどのチェックアウトに属するかをタグ付けします。また、これは 1 対 1 の関係でもありません。一部のファイルを複数のチェックアウトに含めることは完全に合理的です。いずれの場合も、明確化をサポートするために、さまざまなチェックアウトに名前を付ける何らかの方法が必要です。基本的な健全性のために、チェックアウトの関連付けでファイルに注釈を付けるディレクトリ リスターが必要です。

具体的な例として、 を考えてみましょう.gitignore。これは、「すべての」チェックアウトに適用される単一のファイルであり、暗黙的に「一意のチェックアウト」を意味します。うまく機能させるには.gitignore、チェックアウトごとに 1 つ必要です。これは、各チェックアウトで異なるファイルを区別する方法です。しかし、この種のインフラストラクチャのヒントはありませんgit

このような問題を解決する私の「標準的な」方法は、ディレクトリごとに 1 つのチェックアウトになるように配置することです。「custom」などのディレクトリを作成します。そのディレクトリは.gitignore親に入ります。カスタム ディレクトリに、別のリポジトリや別の場所からのチェックアウトを配置しました。つまり、すべての変更をキャプチャするために 2 つのコミットが必要です。それが実際に問題になることはめったにありません。

これが誰かをハッキングするように促す場合はgit、「名前付きチェックアウト」を作成するオプションを追加し、既存の動作を匿名チェックアウトのままにしておきます。[0..1] 個の匿名チェックアウトと [0..*) 個の名前付きチェックアウトを使用できます。その基礎から、あなたが望むかもしれない他のほとんどすべてはかなり自然に続きます。

于 2012-11-13T19:05:05.047 に答える