In a PR, I made changes to some folder dir1 in a bunch of commits:
commit1 changes to dir2, changes to dir3
commit2 changes to dir1, changes to dir3
commit3 changes to dir2, changes to dir3
commit4 changes to dir1, changes to dir3
...
commit30 changes to dir2, changes to dir3
...
commit40 changes to dir2, changes to dir3
...
Now I wish to take back all the changes I made in dir1. But I'm not the only person making changes to dir1 - other people have modified dir1 and merged the changes in master branch.
I have tried:
git checkout master
git pull
git checkout -
git checkout master -- dir1
but on PR page it still shows some changes in dir1 in my PR. I doubt that the comparison might be performed between my PR and the master branch at the time my PR was open. But anyway, how can I reset the folder dir1?
CodePudding user response:
Here are two ways to update the pull request:
Revert the undesired commits
Each commit affecting dir1
should be reverted, as explained in this post.
The revert operation creates new commits undoing the changes of previous commits.
Steps
- start from the branch which was used to create the pull request
- execute
git log
orgit log --oneline
in order to identify the commits which affectdir1
(other commands may be used for this) - execute
git revert <commit-sha>
on each commit affectingdir1
, in chronological order. - push the branch again to the remote repository.
The pull request should be updated and only contain the desired commits.
Rebase the branch
An alternative solution, perhaps more suitable if there are many commits, is to do a mass undo using an interactive rebase.
This techniques rewrites the history of commits (which were already pushed to the remote), so it needs to be performed in a new branch.
steps
- start from the branch which was used to create the pull request
- create a new branch
git checkout -b temp-cleanup
- use git log to identify a commit-sha earlier to the changes you want to undo
- execute
git rebase -i <earlier-sha>
- a text editor opens up, showing the commit history. Delete all lines performing a change to
dir1
(and do not change anything else to the file). - save and exit
- push the newly created branch and create a new pull request using it as a base
- delete the older pull request.
CodePudding user response:
Here's the general method:
Step 1 is to find the commit before
commit1 changes to ...
.Step 2 is to run
git checkout
(Git versions before 2.23) orgit restore
using this commit hash ID.Step 3 is to make a new commit.
In your own case, assuming this:
commit1 changes to dir2, changes to dir3 commit2 changes to dir1, changes to dir3
is an accurate summary of what you did, you can use commit1
directly, rather than finding the commit before commit1
, because every commit holds a full snapshot of every file so the copies of dir1/*
in commit1
simply match the copies of dir/*
in the commit before commit1
. However, as a shortcut, know that adding ^
or ~
as a suffix character to a commit hash ID means the parent of that commit.1
To use git checkout
, use it just as you did:
git checkout <rev> -- dir1
To use git restore
, run it with the -S
and -W
flags (--staged
and --worktree
if you want to spell them out the long way) and --source=rev
. That is, if commit1 is the (abbreviated) hash ID a123456
, you could write:
git restore --source=a123456^ -SW -- dir1
The purpose of the --
here is to make sure that the stuff on the left of the --
is treated as arguments to the command to select the commit that holds the files of interest and tell it where to put the restored files (for git restore
; git checkout
just assumes where to put them), and that stuff on the right of the --
is treated as the set of files to restore. Since dir1
doesn't look like a commit hash ID or a flag, we don't technically need the --
at all, but it's a good habit to get into, in case you ever want to restore a file named -f
or some such.
The general idea here is that by adding a new commit that puts the files back, all the changes you made in between, that changed the files, become irrelevant when merging. Note that this does not work for git rebase
(or GitHub's REBASE AND MERGE button), but only for git merge
; for git rebase
, you have much more work cut out for you.
1More precisely, <rev>^1
means the first parent of the specified revision, <rev>^2
means the second parent of the specified revision, <rev>^3
means the third parent of the specified revision, and so on. However, most commits have only one parent anyway, and omitting the numeric suffix after the ^
"means" 1
. Some command line interpreters may require you to type ^^
rather than just ^
as the CLI itself uses ^
for quoting characters, so you need type two of them to mean one of them. You might want to use ~
for these CLIs since they don't have a special meaning for ~
.
Similarly, <rev>~1
means the first parent of the specified revision, but <rev>~2
means the first parent of the first parent of the specified revision, while <rev>~3
means the first parent of the first parent of the first parent of the specified revision, and so on. That is, this counts commits backwards, one hop at a time, based on the number after the tilde ~
character. As with caret ^
, an omitted number means 1
.
This is discussed in greater detail in the gitrevisions documentation, which is worth a regular (weekly or monthly perhaps) read-through until you really "get it", which takes a long time with Git. There's a lot of stuff in here, so just pick one thing to work on at a time.