O conselho que você recebeu é falho. Configurar incondicionalmente GIT_AUTHOR_DATE em um --env-filter
reescreveria a data de cada confirmação. Além disso, seria incomum usar o git commit dentro --index-filter
.
Você está lidando com vários problemas independentes aqui.
Especificando datas diferentes de "agora"
Cada confirmação tem duas datas: a data do autor e a data do confirmador. Você pode substituir cada um deles fornecendo valores por meio das variáveis de ambiente GIT_AUTHOR_DATE e GIT_COMMITTER_DATE para qualquer comando que grava uma nova confirmação. Veja “Formatos de Data” no git-commit (1) ou abaixo:
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
O único comando que grava uma nova confirmação durante o uso normal é git commit . Ele também possui uma --date
opção que permite especificar diretamente a data do autor. Seu uso antecipado inclui git filter-branch --env-filter
também as variáveis de ambiente mencionadas acima (elas fazem parte do "env" após o qual a opção é nomeada; consulte "Opções" em git-filter-branch (1) e o comando "encanamento" subjacente git-commit -árvore (1) .
Inserindo um arquivo em um único histórico ref
Se o seu repositório é muito simples (ou seja, você possui apenas uma ramificação, sem tags), provavelmente poderá usar o git rebase para fazer o trabalho.
Nos comandos a seguir, use o nome do objeto (hash SHA-1) da confirmação em vez de "A". Não se esqueça de usar um dos métodos de "substituição de data" ao executar o 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
Se você deseja atualizar A para incluir o novo arquivo (em vez de criar um novo commit onde foi adicionado), use em git commit --amend
vez de git commit
. O resultado ficaria assim:
---A'---B'---C'---o---o---o master
O procedimento acima funciona, desde que você possa nomear o commit que deve ser o pai do seu novo commit. Se você realmente deseja que seu novo arquivo seja adicionado por meio de um novo commit raiz (sem pais), precisará de algo um pouco diferente:
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
é relativamente novo (Git 1.7.2), mas existem outras maneiras de fazer a mesma coisa que funcionam em versões mais antigas do Git.
Inserir um arquivo em um histórico multi- referência
Se seu repositório é mais complexo (ou seja, possui mais de uma referência (ramificações, tags, etc.)), provavelmente você precisará usar o git filter-branch . Antes de usar o git filter-branch , você deve fazer uma cópia de backup de todo o seu repositório. Um arquivo tar simples de toda a sua árvore de trabalho (incluindo o diretório .git) é suficiente. O git filter-branch faz refs de backup, mas geralmente é mais fácil se recuperar de uma filtragem não muito correta apenas excluindo seu .git
diretório e restaurando-o do seu backup.
Nota: Os exemplos abaixo usam o comando de nível inferior em git update-index --add
vez de git add
. Você poderia usar o git add , mas primeiro seria necessário copiar o arquivo de algum local externo para o caminho esperado ( --index-filter
executa seu comando em um GIT_WORK_TREE temporário que está vazio).
Se você deseja que seu novo arquivo seja adicionado a cada confirmação existente, você pode fazer o seguinte:
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
Não vejo realmente nenhuma razão para alterar as datas dos commits existentes --env-filter 'GIT_AUTHOR_DATE=…'
. Se você o usasse, o tornaria condicional para que ele reescrevesse a data de cada confirmação.
Se você deseja que seu novo arquivo apareça apenas nas confirmações após alguma confirmação existente ("A"), faça o seguinte:
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
Se você deseja que o arquivo seja adicionado por meio de uma nova confirmação que deve ser inserida no meio do seu histórico, será necessário gerar a nova confirmação antes de usar o git filter-branch e adicionar --parent-filter
ao git filter-branch :
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
Você também pode providenciar para que o arquivo seja adicionado pela primeira vez em uma nova confirmação de raiz: crie sua nova confirmação de raiz através do método “órfão” na seção git rebase (capture-a new_commit
), use o incondicional --index-filter
e --parent-filter
similares "sed -e \"s/^$/-p $new_commit/\""
.