Por que o git não pode fazer redefinições rígidas / flexíveis por caminho?

137

$ git reset -- <file_path> pode redefinir pelo caminho.

No entanto, $ git reset (--hard|--soft) <file_path>reportará um erro como abaixo:

Cannot do hard|soft reset with paths.
yao
fonte

Respostas:

142

Porque não faz sentido (outros comandos já fornecem essa funcionalidade) e reduz o potencial de fazer a coisa errada por acidente.

Uma "reinicialização completa" de um caminho é feita git checkout HEAD -- <path>(verificando a versão existente do arquivo).

Uma reinicialização suave de um caminho não faz sentido.

Uma redefinição mista para um caminho é o que git reset -- <path>faz.

Âmbar
fonte
72
Pessoalmente, acho que git checkout -- <path>deveria ser substituído por git reset --hard <path>. Faz muito mais sentido ...
vergenzt
24
git checkout -- <path>não faz uma reinicialização completa; ele substitui o conteúdo da árvore de trabalho pelo conteúdo preparado. git checkout HEAD -- <path>faz uma redefinição definitiva para um caminho, substituindo o índice e a árvore de trabalho pela versão do commit HEAD.
Dan Fabulich
1
@ EdPlunkett Er, a segunda frase da resposta informa qual outro comando fornece a funcionalidade.
Amber
16
-1: O check-out da referida revisão não removerá os arquivos da cópia de trabalho se a revisão contiver arquivos excluídos. reset --hardcom um caminho forneceria essa peça que faltava. O Git já é tão poderoso que a desculpa "Não permitimos que você faça isso para sua própria proteção" mantém zero de água: existem muitas maneiras de fazer a coisa errada "por acidente". Nada disso importa quando você tem git reflog.
void.pointer
1
como mencionado pelo @ void.pointer, o checkout não removerá os arquivos. Se você deseja esse comportamento, observe esta resposta. Ainda assim, espero que um dia possamos chegar git reset --hard -- <path>. Existem casos de uso legítimos para isso.
Mariusz Pawelski
18

Você pode realizar o que está tentando fazer usando git checkout HEAD <path>.

Dito isto, a mensagem de erro fornecida não faz sentido para mim (como git resetfunciona muito bem em subdiretórios) e não vejo razão para git reset --hardque não deva fazer exatamente o que você está pedindo.

Aaron Kent
fonte
usar o checkout faz as mudanças, o que não é o mesmo que um reset --soft
worc
11

A pergunta como já está respondida , explicarei a parte do porquê .

Então, o que o git reset faz? Dependendo dos parâmetros especificados, ele pode fazer duas coisas diferentes:

  • Se você especificar um caminho, ele substituirá os arquivos correspondentes no índice pelos arquivos de uma confirmação (HEAD por padrão). Essa ação não afeta a árvore de trabalho e geralmente é usada como o oposto do git add.

  • Se você não especificar um caminho, ele moverá o cabeçalho da ramificação atual para uma confirmação especificada e, junto com isso , redefinirá opcionalmente o índice e a árvore de trabalho para o estado dessa confirmação. Esse comportamento adicional é controlado pelo parâmetro mode:
    --soft : não toque no índice e na árvore de trabalho.
    --mixed (padrão): redefine o índice, mas não a árvore de trabalho.
    --hard : redefine o índice e a árvore de trabalho.
    Também existem outras opções, consulte a documentação para obter a lista completa e alguns casos de uso.

    Quando você não especifica uma confirmação, o padrão é HEAD, portanto git reset --soft, nada fará, pois é um comando para mover a cabeça para HEAD (para seu estado atual). git reset --hard, por outro lado, faz sentido devido a seus efeitos colaterais , diz mover a cabeça para HEAD e redefinir o índice e a árvore de trabalho para HEAD.

    Eu acho que agora deve estar claro por que essa operação não é para arquivos específicos por natureza - ela pretende mover uma cabeça de ramificação em primeiro lugar, redefinindo a árvore de trabalho e o índice é uma funcionalidade secundária.

do utilizador
fonte
está claro que a redefinição visa mover uma cabeça de ramificação em primeiro lugar, mas como possui a funcionalidade adicional de redefinir a árvore de trabalho e o índice para confirmações inteiras e a funcionalidade de redefinir o índice para arquivos específicos, por que não tem a funcionalidade de redefinir a árvore de trabalho para arquivos específicos? Eu acredito que é isso que o OP está pedindo.
Danilo Souza Morães
Talvez porque essa funcionalidade (redefinir a árvore de trabalho para arquivos específicos) já esteja disponível como git checkoutcomando? E redefinir para fazer a mesma coisa confundiria ainda mais os usuários. Minha resposta foi que a --hardopção não é aplicável a arquivos específicos porque é um modo de redefinição de ramificação, não de redefinição de índice. E a redefinição da árvore de trabalho é denominada checkout, como você pode ler em outras respostas. Tudo isso é apenas um design ruim da interface do usuário do Git, IMHO.
usuário
Comparando a primeira opção com git checkout: git reset --define apenas o índice, enquanto git checkout --define apenas a árvore de trabalho?
seeker_of_bacon
4

Certifique-se de colocar uma barra entre a origem ou a montante (origem) e a ramificação real:

git reset --hard origin/branch

ou

git reset --hard upstream/branch`
Pascal Nitcheu
fonte
Por favor, leia a pergunta novamente.
Xerus 18/06
3

Há uma razão muito importante por trás disso: os princípios de checkoutereset .

Em termos do Git, checkout significa "trazer para a árvore de trabalho atual". E com git checkoutisso podemos preencher a árvore de trabalho com dados de qualquer área, seja de uma confirmação no repositório ou de arquivos individuais de uma confirmação ou da área de preparação (que é o padrão).

Por sua vez, o git reset não tem essa função. Como o nome sugere, ele redefinirá a ref atual, mas sempre terá o repositório como fonte, independentemente do "alcance" (--soft, --mixed ou --hard).

Recapitular:

  • checkout : de qualquer lugar (index / repo commit) -> árvore de trabalho
  • reset : Repo commit -> Substituir HEAD (e opcionalmente indexar e árvore de trabalho)

Portanto, o que pode ser um pouco confuso é a existência de, git reset COMMIT -- filesjá que "sobrescrever HEAD" com apenas alguns arquivos não faz sentido!

Na ausência de uma explicação oficial, posso apenas especular que os desenvolvedores do git descobriram que resetainda era o melhor nome de um comando para descartar as alterações feitas na área de preparação e, dada a única fonte de dados que era o repositório, " vamos estender o funcionalidade "em vez de criar um novo comando.

Então, de alguma forma, git reset -- <files>já é um pouco excepcional: não substitui o HEAD. IMHO todas essas variações seriam exceções. Mesmo que possamos conceber uma --hardversão, outras (por exemplo --soft) não fazem sentido.

F Pereira
fonte
Eu gosto desta resposta. Realmente, git reset -- <files>caiu como se fosse adicionado, porque esse é um recurso útil, mas ninguém tinha certeza de qual comando deveria ser colocado. Felizmente agora temos muito mais são, git restoreque têm funcionalidade git checkout -- <path> git checkout <commit> -- <path>e git reset [<commit>] -- <path>com muitos padrões mais saudáveis ​​e ainda mais recursos que você não poderia fazer antes (ao contrário do que a resposta aceita diz. Agora você pode finalmente restaurar facilmente apenas a árvore de trabalho, sem tocar no índice).
Mariusz Pawelski 25/03
0

Explicação

O git resetmanual lista três maneiras de invocação:

  • 2 são de arquivo: Eles não afetam a árvore de trabalho , mas operam apenas nos arquivos no índice especificado por <paths>:

    • git reset [-q] [<tree-ish>] [--] <paths>..
    • git reset (--patch | -p) [<tree-ish>] [--] [<paths>...]
  • 1 é de confirmação: opera em todos os arquivos no referenciado <commit>e pode afetar a árvore de trabalho:

    • git reset [<mode>] [<commit>]

Não há modo de chamada que opera apenas em arquivos especificados e afeta a árvore de trabalho.

Gambiarra

Se você deseja ambos:

  • Redefinir a versão do índice / cache de um arquivo
  • Faça o check-out do (s) arquivo (s) (por exemplo, faça a árvore de trabalho corresponder ao índice e confirme a versão)

Você pode usar esse alias no seu arquivo de configuração do git:

[alias]
  reco   = !"cd \"${GIT_PREFIX:-.}\" && git reset \"$@\" && git checkout \"$@\" && git status --short #"  # Avoid: "fatal: Cannot do hard reset with paths."

Você pode fazer um dos seguintes:

$ git reco <paths>

$ git reco <branch/commit> <paths>

$ git reco -- <paths>

(Mnenônico para reco: reset && check out)

Tom Hale
fonte
-2

O nome do arquivo git reset --soft HEAD ~ 1 desfaz a confirmação, mas as alterações permanecem no local. filename poderia ser - para todos os arquivos confirmados

Peiti Li
fonte
6
fatalCannot do soft reset with paths.
alt