'Git Pull Force to overwrite local files

"Git Pull Force", "git reset branch to origin" or in other words, to pull a remote branch to overwrite a local branch, seems to be wildly searched feature with an increasing interest despite few local declines.

And it absolutely makes sense with growing teams and ever increasing number of developers.

enter image description here

Currently, the shortest working solution is quite verbose and requires knowledge of the branch

git reset --hard origin/<branch_name>

edit: There is a more convenient variant git reset --hard @{u}.
Please give credit where it is due comment. More shortcuts here.

which is unfortunate, as typing the following is so much faster

git pull

which, however, brings its own challenges. Diverging histories, merge conflicts, etc...


We do have shorthands such as this

git push origin HEAD -u --force

which pushes a local branch <branch_name> to an origin, overwrites a remote branch with same name <branch_name> and sets it as it's own upstream branch.

However, there is no such --force/reset alternative to git pull.


What would be the best way to have this feature added to git?


How do I force "git pull" to overwrite local files? 6.6m views
Reset local repository branch to be just like remote repository HEAD 4.7m views
How do I force git pull to overwrite everything on every pull? 370k views
Resolve conflicts using remote changes when pulling from Git remote 240k views
How to force update when doing git pull? 90k views
Force GIT Pull without commiting
Force a pull with git
git force pull with implicit rebase
Clean up a fork and restart it from the upstream
Force git to update my local repo when pulling
Reset all branches of a local repo to be the same as remote
Github - Discard all changes

git


Solution 1:[1]

I'm going to take a different approach, by challenging the premises of the question. If you find yourself having to adjust your local branch to match the remote branch, this suggests that you should never have had a local branch to start with.

Instead, work in such a way that the branch you work with is the remote tracking branch, and so there is nothing to adjust.

Take, for example, me. I work all day nimbly making little feature branches and pushing them to form pull requests to be merged to main. But I myself have no main. What do I need it for? Nothing!

  • To start a feature branch, I fetch and then create the branch starting at origin/main.
  • To study the state of the remote main, I fetch and then check out origin/main, detached.
  • To review a pull request, I fetch and then check out origin/yourbranch, detached.

So the only local branches I ever have are my own feature branches — and they have no upstream and cannot be pulled (and don't need to be, as the only further commits in them will emanate from me).

In short, I'm suggesting that if it is genuinely true that people want to know how "to pull a remote branch to overwrite a local branch" as part of their regular workflow, this merely shows they are using Git wrong to begin with. You should never make a local branch unless:

  • You are going to add commits to it and push it, or
  • You are going to merge into it locally, which is not generally the case in today's world of pull requests, or
  • It is purely local and experimental, ie you're just trying a possible line of approach while developing a feature.

None of those scenarios will ever involve a corresponding remote tracking branch, so the situation posited in the question will never arise.

Solution 2:[2]

I would underline that, while you certainly mention a need that arises daily, git commands that forcibly delete your work without warning are also a source of questions, with much more damageable consequences (look for "I lost my work after git reset --hard / git checkout ., can I get it back ?" questions).

With your pull -f example, this would be amplified by the fact that, when you inetract with a remote, you don't know what you are going to get from the remote.

Based on my humble experience, I stronly suggest to take the habbit of not using git pull and only use git fetch.

Then inspect the differences with origin/branchname, and then choose whether you want to reset or rebase or ...


It would be nice to have a command to say "move to that commit and discard all changes" in one go, I'll repeat here what I suggested in a comment to @VonC's answer :

#!/bin/bash

target=$1
if [ -z "$target" ]; then
    target=HEAD
fi

set -e  # avoid going forward if one command fails ...

git stash
git restore -SW -s "$target" -- .
git reset "$target"

(I don't have a good alias name for that : git goto ?)

It would give a reasonably safe alternative to git reset ; the main caveat being : a file on disk, not tracked in the starting commit but tracked in $target will be overwritten without being saved.

You would need a more convoluted variant of git stash to save those files too.


[update] I think I found a script that manages to stash away the impacted files (and only those) :

#!/bin/bash

target=$1
if [ -z "$target" ]; then
    target=HEAD
fi

set -e  # avoid going forward if one command fails ...

list_impacted_files () {
   local target=$1

   # tracked files in the working tree that have a diff
   git diff --no-renames --name-only HEAD

   # untracked files that will be clobbered when restoring $target :
   #   * files that are present in $target but not in HEAD
   #   * and that currently exist on disk (use 'ls' to keep only those)
   # add '|| true' to ignore error code returned by 'ls' on non existing files
   git diff --no-renames --name-only --diff-filter=A HEAD "$target" |\
     xargs -r ls 2> /dev/null || true 
}

# list files as described above, and feed this list to `git stash -u`
# 'xargs -r <cmd>' avoids running <cmd> at all if stdin is empty
# in our case: don't run 'git stash -u' if no files are to be stashed ...
list_impacted_files "$target" | xargs -r git stash -u --

git restore -SW -s "$target" -- .
git reset "$target"

To restore back your repo to how it was :

  • revert to the initial commit (using git reset or the script above ...)
  • spot the stash@{xx} you want to restore in git stash list
  • run git stash apply --index stash@{xx}

Solution 3:[3]

You need at least three commands:

  • a fetch, to update the remote tracking branches
  • a reset to the upstream branch, using the @{u} or @{upstream} suffix
  • a clean to make sure no extra files are left

That is:

git fetch
git reset --hard @{u}
git clean -nd

(replace in git clean the -nd option by -fd to actually remove the files: I always prefer to first preview what a git clean will do, before actually deleting anything)

You would have to group those as an alias or a script (git-pullreset), that you can then call.


LeGEC suggests in the comments:

git stash
git restore -SW -s @{u} -- .
git reset @{u}

That would:

  • leave completely untracked files (e.g: files not tracked in HEAD nor in @{u}) untouched, and
  • would give a way to still have something to revert to "how it was before".

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 matt
Solution 2
Solution 3 LeGEC