306

私は自分の個人的な使用のためにすべてをGitに変換していますが、リポジトリにすでに古いバージョンのファイルがいくつか見つかりました。ファイルの「変更日」に従って正しい順序で履歴にコミットして、ファイルの正確な履歴を取得するにはどうすればよいですか?

私はこのようなことがうまくいくと言われました:

git filter-branch --env-filter="GIT_AUTHOR_DATE=... --index-filter "git commit path/to/file --date " --tag-name-filter cat -- --all  
4

12 に答える 12

237

あなたが与えられたアドバイスには欠陥があります。にGIT_AUTHOR_DATEを無条件に設定する--env-filterと、すべてのコミットの日付が書き換えられます。また、内部でgitcommitを使用することは珍しいでしょう--index-filter

ここでは、複数の独立した問題を扱っています。

「現在」以外の日付の指定</h1>

各コミットには、作成者の日付とコミッターの日付の2つの日付があります。新しいコミットを書き込むコマンドの環境変数GIT_AUTHOR_DATEおよびGIT_COMMITTER_DATEを介して値を指定することにより、それぞれをオーバーライドできます。git-commit(1)または以下の「日付形式」を参照してください。

Git internal format = <unix timestamp> <time zone offset>, e.g.  1112926393 +0200
RFC 2822            = e.g. Thu, 07 Apr 2005 22:13:13 +0200
ISO 8601            = e.g. 2005-04-07T22:13:13

通常の使用中に新しいコミットを書き込む唯一のコマンドはgitcommitです。また--date、作成者の日付を直接指定できるオプションもあります。予想される使用法にはgit filter-branch --env-filter、上記の環境変数も含まれます(これらは、オプションの名前が付けられた後の「env」の一部です。git-filter-branch(1)の「Options」および基礎となる「plumbing」コマンドgit-commitを参照してください)。 -tree(1)

単一の参照履歴へのファイルの挿入

リポジトリが非常に単純な場合(つまり、ブランチが1つだけで、タグがない場合)、おそらくgitrebaseを使用して作業を行うことができます。

次のコマンドでは、「A」の代わりにコミットのオブジェクト名(SHA-1ハッシュ)を使用します。git commitを実行するときは、「日付オーバーライド」メソッドの1つを使用することを忘れないでください。

---A---B---C---o---o---o   master

git checkout master
git checkout A~0
git add path/to/file
git commit --date='whenever'
git tag ,new-commit -m'delete me later'
git checkout -
git rebase --onto ,new-commit A
git tag -d ,new-commit

---A---N                      (was ",new-commit", but we delete the tag)
        \
         B'---C'---o---o---o   master

(ファイルが追加された場所に新しいコミットを作成する代わりに)新しいファイルを含めるようにAを更新する場合は、のgit commit --amend代わりにを使用しgit commitます。結果は次のようになります。

---A'---B'---C'---o---o---o   master

上記は、新しいコミットの親となるコミットに名前を付けることができる限り機能します。新しいルートコミット(親なし)を介して新しいファイルを実際に追加する場合は、少し異なるものが必要です。

B---C---o---o---o   master

git checkout master
git checkout --orphan new-root
git rm -rf .
git add path/to/file
GIT_AUTHOR_DATE='whenever' git commit
git checkout -
git rebase --root --onto new-root
git branch -d new-root

N                       (was new-root, but we deleted it)
 \
  B'---C'---o---o---o   master

git checkout --orphanは比較的新しい(Git 1.7.2)ですが、古いバージョンのGitで機能する同じことを行う他の方法があります。

マルチリファレンス履歴へのファイルの挿入

リポジトリがより複雑な場合(つまり、複数の参照(ブランチ、タグなど)がある場合)、おそらくgitfilter-branchを使用する必要があります。git filter-branchを使用する前に、リポジトリ全体のバックアップコピーを作成する必要があります。作業ツリー全体(.gitディレクトリを含む)の単純なtarアーカイブで十分です。git filter-branch.gitはバックアップ参照を作成しますが、ディレクトリを削除してバックアップから復元するだけで、正しくないフィルタリングから回復する方が簡単な場合がよくあります。

git update-index --add注:以下の例では、の代わりに下位レベルのコマンドを使用していますgit addgit addを使用することもできますが、最初にファイルを外部の場所から予期されたパスにコピーする必要があります(--index-filter空の一時的なGIT_WORK_TREEでコマンドを実行します)。

新しいファイルを既存のすべてのコミットに追加する場合は、次のようにします。

new_file=$(git hash-object -w path/to/file)
git filter-branch \
  --index-filter \
    'git update-index --add --cacheinfo 100644 '"$new_file"' path/to/file' \
  --tag-name-filter cat \
  -- --all
git reset --hard

既存のコミットの日付を。で変更する理由は実際にはわかりません--env-filter 'GIT_AUTHOR_DATE=…'。使用した場合は、すべてのコミットの日付を書き換えるように条件付きにする必要があります。

新しいファイルを既存のコミット(「A」)の後のコミットにのみ表示する場合は、次のようにします。

file_path=path/to/file
before_commit=$(git rev-parse --verify A)
file_blob=$(git hash-object -w "$file_path")
git filter-branch \
  --index-filter '

    if x=$(git rev-list -1 "$GIT_COMMIT" --not '"$before_commit"') &&
       test -n "$x"; then
         git update-index --add --cacheinfo 100644 '"$file_blob $file_path"'
    fi

  ' \
  --tag-name-filter cat \
  -- --all
git reset --hard

履歴の途中に挿入される新しいコミットを介してファイルを追加する場合は、git filter-branchを使用する前に新しいコミットを生成し、gitfilter-branchに追加する必要あります。--parent-filter

file_path=path/to/file
before_commit=$(git rev-parse --verify A)

git checkout master
git checkout "$before_commit"
git add "$file_path"
git commit --date='whenever'
new_commit=$(git rev-parse --verify HEAD)
file_blob=$(git rev-parse --verify HEAD:"$file_path")
git checkout -

git filter-branch \
  --parent-filter "sed -e s/$before_commit/$new_commit/g" \
  --index-filter '

    if x=$(git rev-list -1 "$GIT_COMMIT" --not '"$new_commit"') &&
       test -n "$x"; then
         git update-index --add --cacheinfo 100644 '"$file_blob $file_path"'
    fi

  ' \
  --tag-name-filter cat \
  -- --all
git reset --hard

新しいルートコミットでファイルを最初に追加するように調整することもできます。gitrebaseセクションから「orphan」メソッドを使用して新しいルートコミットを作成し(でキャプチャしnew_commitます)、無条件--index-filterなどを使用し--parent-filterます"sed -e \"s/^$/-p $new_commit/\""

于 2010-10-10T03:03:08.090 に答える
165

通常どおりコミットを作成できますが、コミットするときに、環境変数GIT_AUTHOR_DATEGIT_COMMITTER_DATE適切な日時に設定します。

もちろん、これにより、ブランチの先端 (つまり、現在の HEAD コミットの前) でコミットが行われます。リポジトリでさらにプッシュしたい場合は、少し工夫する必要があります。あなたがこの歴史を持っているとしましょう:

o--o--o--o--o

そして、新しいコミット (「X」とマーク) を2 番目に表示する必要があります。

o--X--o--o--o--o

最も簡単な方法は、最初のコミットから分岐し、新しいコミットを追加してから、他のすべてのコミットを新しいコミットの上にリベースすることです。そのようです:

$ git checkout -b new_commit $desired_parent_of_new_commit
$ git add new_file
$ GIT_AUTHOR_DATE='your date' GIT_COMMITTER_DATE='your date' git commit -m 'new (old) files'
$ git checkout master
$ git rebase new_commit
$ git branch -d new_commit
于 2010-10-09T15:16:12.513 に答える
42

私の場合、myfileの一連のバージョンをmyfile_bak、myfile_old、myfile_2010、backups/myfile などとして保存していました。変更日を使用して、myfile の履歴を git に保存したいと考えていました。したがって、最も古いものの名前を myfile に 変更しgit add myfile、次にgit commit --date=(modification date from ls -l) myfile、次に古い名前を myfile に変更し、 --date を使用して別の git commit を繰り返します...

これをいくらか自動化するには、shell-foo を使用してファイルの変更時刻を取得します。私はls -landから始めましcutたが、 stat(1) はより直接的です

git commit --date="`stat -c %y myfile `" myfile

于 2012-03-19T22:33:54.650 に答える
21

私の場合、 --date オプションを使用しているときに、git プロセスがクラッシュしました。ひどいことをしたのかもしれません。その結果、いくつかの index.lock ファイルが表示されました。そのため、.git フォルダーから .lock ファイルを手動で削除して実行し、変更されたすべてのファイルが渡された日付にコミットされ、今回は機能しました。ここですべての答えをありがとう。

git commit --date="`date --date='2 day ago'`" -am "update"
于 2013-10-30T11:03:45.870 に答える
7

または、fake-git-historyを使用して、特定のデータ範囲に対して生成します。

于 2019-08-17T05:57:46.200 に答える