Why git can't do hard/soft resets by path?

前端 未结 7 1105
面向向阳花
面向向阳花 2020-12-07 17:33

$ git reset -- can reset by path.

However, $ git reset (--hard|--soft) will report an error like below:

相关标签:
7条回答
  • 2020-12-07 17:41

    Explanation

    The git reset manual lists 3 ways of invocation:

    • 2 are file-wise: These do not affect the working tree, but operate only on the files in the index specified by <paths>:

      • git reset [-q] [<tree-ish>] [--] <paths>..
      • git reset (--patch | -p) [<tree-ish>] [--] [<paths>...]
    • 1 is commit-wise: Operates on all files in the referenced <commit>, and may affect the working tree:

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

    There's no mode of invocation that operates only on specified files and affects the working tree.

    Workaround

    If you want to both:

    • Reset the index/cache version of a file(s)
    • Checkout the file(s) (ie, make the working tree match the index and commit version)

    You can use this alias in your git config file:

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

    You can then do one of:

    $ git reco <paths>
    
    $ git reco <branch/commit> <paths>
    
    $ git reco -- <paths>
    

    (Mnenonic for reco: reset && checkout)

    0 讨论(0)
  • 2020-12-07 17:50

    Because there's no point (other commands provide that functionality already), and it reduces the potential for doing the wrong thing by accident.

    A "hard reset" for a path is just done with git checkout HEAD -- <path> (checking out the existing version of the file).

    A soft reset for a path doesn't make sense.

    A mixed reset for a path is what git reset -- <path> does.

    0 讨论(0)
  • 2020-12-07 17:51

    There's a very important reason behind that: the principles of checkout and reset.

    In Git terms, checkout means "bring into the current working tree". And with git checkout we can fill the working tree with data from any area, being it from a commit in the repository or individual files from a commit or the staging area (which is the even the default).

    In turn, git reset doesn't have this role. As the name suggests, it will reset the current ref but always having the repository as a source, independently of the "reach" (--soft, --mixed or --hard).

    Recap:

    • checkout: From anywhere (index / repo commit) -> working tree
    • reset: Repo commit -> Overwrite HEAD (and optionally index and working tree)

    Therefore what can be a bit confusing is the existence of git reset COMMIT -- files since "overwriting HEAD" with only some files doesn't make sense!

    In the absence of an official explanation, I can only speculate that the git developers found that reset was still the best name of a command to discard changes made to the staging area and, given the only data source was the repository, then "let's extend the functionality" instead of creating a new command.

    So somehow git reset -- <files> is already a bit exceptional: it won't overwrite the HEAD. IMHO all such variations would be exceptions. Even if we can conceive a --hard version, others (for example --soft) wouldn't make sense.

    0 讨论(0)
  • 2020-12-07 17:52

    git reset --soft HEAD~1 filename undo the commit but changes remain in local. filename could be -- for all commited files

    0 讨论(0)
  • 2020-12-07 17:54

    Make sure you put a slash between origin or upstream (source) and the actual branch:

    git reset --hard origin/branch
    

    or

    git reset --hard upstream/branch`
    
    0 讨论(0)
  • 2020-12-07 17:56

    You can accomplishment what you're trying to do using git checkout HEAD <path>.

    That said, the provided error message makes no sense to me (as git reset works just fine on subdirectories), and I see no reason why git reset --hard shouldn't do exactly what you're asking of it.

    0 讨论(0)
提交回复
热议问题