Ответ 1
Совет, который вам дал, испорчен. Безусловно, установка GIT_AUTHOR_DATE в --env-filter
будет переписывать дату каждой фиксации. Кроме того, было бы необычно использовать git commit внутри --index-filter
.
Здесь вы имеете дело с несколькими независимыми проблемами.
Задание дат, отличных от "сейчас"
Каждая фиксация имеет две даты: дату автора и дату коммиттера. Вы можете переопределить каждый из них, передавая значения через переменные среды GIT_AUTHOR_DATE и GIT_COMMITTER_DATE для любой команды, которая записывает новую фиксацию. См. "Форматы даты" в git -комментировать (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).
Вставка файла в одну историю ссылок
Если ваш репозиторий очень прост (т.е. у вас есть только одна ветка, нет тегов), то вы, вероятно, можете использовать git rebase для выполнения этой работы.
В следующих командах используйте имя объекта (SHA-1 хеш) для фиксации вместо "A". Не забудьте использовать один из методов "переопределения даты" при запуске 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. Прежде чем использовать ветвь фильтра git, вы должны сделать резервную копию всего своего репозитория. Простой tar-архив всего вашего рабочего дерева (включая каталог .git) достаточно. 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=…'
. Если вы его использовали, вы бы сделали его условным, чтобы он переписывал дату для каждой фиксации.
Если вы хотите, чтобы ваш новый файл появлялся только в коммитах после некоторого существующего commit ( "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 rebase (запишите его в new_commit
), используйте безусловный --index-filter
и a --parent-filter
как "sed -e \"s/^$/-p $new_commit/\""
.