過去にGitをコミットするにはどうすればよいですか?


222

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

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

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

短くてシンプルな答え:stackoverflow.com/a/34639957/2708266
Yash

33
これに対する答えを探している人々は、GitHubの「
コントリビューション

1
@ZitRoはい。そしてgit commit --date="xxx day ago" -m "yyy"、誰かが疑問に思っているなら、単に設定するだけでその目的には十分です。
Vlas Sokolov

alexpeattie.com/blog/working-with-dates-in-git:穏やかな説明を探している場合
thepurpleowl

回答:


198

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

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

「現在」以外の日付の指定

各コミットには、作成者の日付とコミッターの日付の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

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

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

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

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

---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の古いバージョンで動作する同じこと行う他の方法があります

マルチ参照履歴へのファイルの挿入

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

注:以下の例では、のgit update-index --add代わりに下位レベルのコマンドを使用していgit addます。git 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のフィルタ分岐をして追加--parent-filterするgitのフィルタ分岐

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

「孤児」メソッドを介してコミットして新しいルートを作成します。また、ファイルが最初にコミットする新しいルートに追加するために手配することができGitのリベースのセクション(キャプチャそれでnew_commit無条件を使用し、) --index-filter、および--parent-filterように"sed -e \"s/^$/-p $new_commit/\""


ユースケースの最初の例である「Multi-ref Historyへのファイルの挿入」:--index-filterによって返されるコミットに適用する方法はありgit rev-listますか?現時点では、rev-listのサブセットに適用されたインデックスフィルターが表示されています。洞察を感謝します。
ハリネズミ

@Hedgehog:すべての「複数参照」の例は-- --all、任意の参照から到達可能なすべてのコミットを処理するために使用します。最後の例は、特定のコミットのみを変更する方法を示しています(好きなだけGIT_COMMITをテストしてください)。コミットの特定のリストのみを変更するには、フィルターする前にリストを保存し(例git rev-list … >/tmp/commits_to_rewrite:)、フィルター内のメンバーシップをテストします(例:)if grep -qF "$GIT_COMMIT" /tmp/commits_to_rewrite; then git update-index …。正確には何を達成しようとしていますか?コメントで説明するには多すぎる場合は、新しい質問を開始することができます。
Chris Johnsen、2010

私の場合、ソース管理下に置くつもりだった小さなプロジェクトがあります。(私はそうしていませんでした。それはそれがソロプロジェクトであり、どのシステムを使用するかを決めていなかったためです)。私の "ソース管理"は、プロジェクトツリー全体を新しいディレクトリに複製し、以前のバージョンのディレクトリの名前を変更するだけでした。私はGitを初めて使用する(以前はSCCS、RCS、およびCVSに似た醜い専用のRCS派生物を使用していた)ので、私に話しかけることを心配しないでください。答えには、「単一の参照履歴へのファイルの挿入」セクションのバリエーションが含まれているという印象を受けます。正しい?
Steve

1
@Steve:「single-ref」シナリオは、履歴が単一の開発ラインからのものである場合に適用されます(つまり、すべてのスナップショットが単一の線形ブランチの連続したポイントとして表示される場合に意味があります)。「マルチリファレンス」シナリオは、履歴に複数のブランチがある(または持っていた)場合に適用されます。履歴が複雑でない限り(さまざまなクライアントの「分岐」バージョン、「バグ修正」ブランチを維持しながら「開発」などで新しい作業が発生した場合など)、おそらく「単一参照」の状況になっています。しかし、あなたの状況は質問の状況とは異なるようです…
クリスジョンセン2014

1
@Steve:一連の履歴ディレクトリスナップショットがあり、Gitリポジトリがまだないためimport-directories.perl、Git contrib/(またはimport-tars.perl、またはimport-zips.py…)から使用して、スナップショットから新しいGitリポジトリを作成できます( 「古い」タイムスタンプ)。私の回答のrebase/ filter-branchテクニックは、既存のリポジトリの履歴から「省略」されたファイルを挿入する場合にのみ必要です。
Chris Johnsen、2014

119

通常どおりコミットを作成できますが、コミットするときは、環境変数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

25
私は常にこの良い答えを見つけますが、日付形式を見つけるために急いでオフにする必要があります。そのため、次回は 'Fri Jul 26 19:32:10 2013 -0400'
MeBigFatGuy

94
GIT_AUTHOR_DATE='Fri Jul 26 19:32:10 2013 -0400' GIT_COMMITTER_DATE='Fri Jul 26 19:32:10 2013 -0400' git commit
Xeoncross 2013年

15
むしろ、ニーモニック例えば、より多くあります2013-07-26T19:32:10kernel.org/pub/software/scm/git/docs/...
マリアン

3
ちなみに、 '-0400'の部分はタイムゾーンオフセットを指定しています。適切に選択することに注意してください。そうでない場合、それに基づいて時間が変更されます。たとえば、シカゴに住んでいる私自身の場合、「-0600」(北米CST)を選択する必要がありました。コードはこちらで確認できます: timeanddate.com/time/zones
evaldeslacasa

2
繰り返される日付のニーズいるので、私はそれが簡単に別の変数を作成することが分かっ:THE_TIME='2019-03-30T8:20:00 -0500' GIT_AUTHOR_DATE=$THE_TIME GIT_COMMITTER_DATE=$THE_TIME git commit -m 'commit message here'
フランクHenard

118

私はこの質問がかなり古いことを知っていますが、それが実際に私のために働いたものです:

git commit --date="10 day ago" -m "Your commit message" 

16
これは完全に機能しました。--date人間が読める相対日付形式もサポートしていることに驚いています。
ZitRo、

@ZitRoなぜ10日ではないのですか?
Alex78191

10
警告:git --date$ GIT_AUTHOR_DATEのみを変更します...状況に応じて、コミットに添付された現在の日付($ GIT_COMMITTER_DATE)が表示されます
Guido U. Draheim

3
@ GuidoU.Draheimが言ったことにタグ付けします。を使用して、コミットの完全な詳細を確認できgit show <commit-hash> --format=fullerます。そこにAuthorDateは、指定した日付が表示されますCommitDateが、実際のコミット日付です。
d4nyll 2018

それで、CommitDateを変更したり、完全に過去の日付のコミットをしたりする方法はありませんか?@ d4nyll
Rahul

35

私の場合、時間の経過とともに、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コミットを繰り返します...

これを多少自動化するには、shell-fooを使用してファイルの変更時間を取得します。私はで開始ls -lしてcut、しかしSTAT(1)は、より直接的です

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


1
いいね。私は確かに私のgit日の前からファイルの変更時刻を使用したいファイルを持っています。ただし、これはコミット日付(作成者日付ではない)を設定するだけではありませんか?
Xeoncross '26年

git-scm.com/docs/git-commitから:--date「コミットで使用された作成者の日付を上書きします。」 git logの日付はAuthorDateのgit log --pretty=fullerようで、AuthorDateとCommitDateの両方が表示されます。
スキーヤーページ

16
git commitオプションは--dateのみ変更されますGIT_AUTHOR_DATE、ではありませんGIT_COMMITTER_DATEプロGitの書籍が説明:「コミッターが最後の仕事を適用した人であるのに対し、著者は、もともと仕事を書いた人です。」日付のコンテキストでは、はGIT_AUTHOR_DATEファイルが変更された日付ですが、GIT_COMMITTER_DATEはコミットされた日付です。ここでは、デフォルトで作成git log者の日付を「日付」として表示しますが、--sinceオプションを指定すると、コミット日付を使用してフィルタリングすることに注意してください。
クリストファー

OS X 10.11.1のstatには-cオプションはありません
thinsoldier

stat -c %ymacOS(およびその他のBSDバリアント)のと同等のものはstat -f %mです。
勝利

21

以下は、私が上の変更をコミットするために使用するものであるfooN=1、過去の日:

git add foo
git commit -m "Update foo"
git commit --amend --date="$(date -v-1d)"

さらに古い日付、たとえば3日前にコミットする場合は、date引数を変更しますdate -v-3d

たとえば、昨日何かをコミットするのを忘れたときなどにとても便利です。

UPDATE:またはの--dateような式も受け入れます。したがって、これを1行のコマンドに減らすことができます。--date "3 days ago"--date "yesterday"

git add foo ; git commit --date "yesterday" -m "Update"

6
警告:git --date$ GIT_AUTHOR_DATEのみを変更します...状況に応じて、コミットに添付された現在の日付($ GIT_COMMITTER_DATE)が表示されます
Guido U. Draheim

混合2は、一緒に近づき、ほぼ完璧にフィットします:git commit --amend --date="$(stat -c %y fileToCopyMTimeFrom)"
アンドレイ

驚くばかり!人間が読める日付の同じコマンドgit commit --amend --date="$(date -R -d '2020-06-15 16:31')"
pixelbrackets

16

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

git commit --date="`date --date='2 day ago'`" -am "update"

5
上記のコメントをgit commit --date参照してください。また、サンプルのコミットメッセージは、@ JstRoRRのような貧弱な1行メッセージを阻止するように修正する必要があります
Christopher

4
警告:git --date$ GIT_AUTHOR_DATEのみを変更します...状況に応じて、コミットに添付された現在の日付が表示されます($ GIT_COMMITTER_DATE)
Guido U. Draheim

9

過去に行われたように見えるコミットを行うには、次の両方を設定する必要がGIT_AUTHOR_DATEありGIT_COMMITTER_DATEます。

GIT_AUTHOR_DATE=$(date -d'...') GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE" git commit -m '...'

where date -d'...'はのような正確な日付、2019-01-01 12:00:00またはのような相対です5 months ago 24 days ago

git logで両方の日付を表示するには、次を使用します。

git log --pretty=fuller

これはマージコミットでも機能します:

GIT_AUTHOR_DATE=$(date -d'...') GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE" git merge <branchname> --no-ff

2

コンピューターの日付をいつでも変更してコミットし、日付を変更してプッシュできます。


1
私はそれが正しい習慣ではないと思います、それはいくつかの未知の問題を引き起こすかもしれません
Vino

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.