Reputation: 287
I cannot find a way to delete a targeted commit from the log. Documentation about deleting commits either just resets a pointer using reset (which does not delete anything, it just resets pointers), or rebase, which creates a new structure of some sort at the deleted commit. The hash of the targeted commit still appears. There may be old documentation out there related to
git rebase -i fork1~1 fork1~3
because in older messages, it states to delete a line in the editor, that is, the line corresponding to the targeted commit. When I get into the interactive editor, one of the choices is "drop", which it says removes the commit. However, I have not successfully removed any commits in the sense that they disappear from the log. I insert the word drop, and it creates some sort of fork-looking thing in the log output. But I can still see the hash of the commit I am trying to remove. Simply put, I want to identify a hash to remove and have it no longer appear in the log.
So, the idea here is to drop 1bc7112:
* 4518859 (refs/stash) WIP on fork3: 8c438a9 Added first.bsh back in
|\
| * 1bc7112 index on fork3: 8c438a9 Added first.bsh back in
|/
* 8c438a9 Added first.bsh back in
* eef828f (HEAD -> fork4) Removed using git rm
* c21cda0 Third change.
Upvotes: 1
Views: 1133
Reputation: 535556
Now that you have posted the log (as a graph), the reason is clear: the commit you want to drop is not part of the history at all. It's being preserved by the stash. The way to delete it is to drop the stash itself.
The graph reveals what an odd duck the stash really is. It is in fact a merge commit; its first parent is the HEAD when you created the stash (because the working tree existed with regard to that HEAD) and its second parent is the entire index at that moment, and the parent of that second parent is the HEAD. The documentation draws it like this:
.----W
/ /
-----H----I
The dot is the stash, H is the HEAD, I is the recorded index, and W is the recorded worktree.
So as long as the stash exists, that H commit (the one that was head at the time you stashed) cannot go out of existence.
Meanwhile, the commit you are complaining about isn't even a real commit! It's the frozen snapshot of the Index at the moment you made the stash. It is integral to the stash.
Upvotes: 3
Reputation: 61935
That is expected behavior.
Git is basically a simple directed graph where branches are auto-advanced names (aka refs) to commits, and thus a head node in some graph subtree. A reset is one method to change which commit a branch names.
Likewise, when a rebase is performed, changes that result in a different hash calculation result in a distinct commits. Parent hashes are also part of the calculation, which is why dropping a commit affects all descendants. The branch is then updated to point to the new commit tree’s head, while the original commits themselves are not actually removed from the set of all commits and can still be found via the reflog.
After a reset or rebase these outdated (including dropped) commits are “orphans” if and only if they cannot be referenced by any refs+. The commits can still be referenced by commit hash as they still exist on disk; however, if there are no direct refs to these commits, and they are not an ancestor of any refs, they are not expected to show up in the log command.
+Git refs (aka references) include ~branches~, ~tags~, and ~stash entries~.
If commits are not “orphaned”, and thus visible in the normal log, there is a branch or tag that needs to be updated. This also includes remote tracking branches and stash entries. Update and/or remove these refs as relevant. If the branch keeping these commits from being “orphaned” is already pushed and on a mainline .. well, let’s hope that is not the case.
Removing a commit from disk, after it has been made an orphan can be done via the prune command which can delete all orphans. Git normally does periodic GC housekeeping so this is not normally required.
If the commit has already been pushed, this process must be propagated to every repository that now has the change. If this involves force-pushing mainline branches it may be "very annoying" to other developers and/or infeasible, depending on how many other commits are descendants. This should only be done as last resort as commit (and any data therein) is already leaked and a potential dependency of other remote commits.
Upvotes: 0
Reputation: 135
When making a git rebase -i HEAD~X
where X
is the number of commits to rebase, you can rebase in interactive mode. You'll see (for example) the following options :
pick 0a3e7b9 other commit
pick 7cfa14c commit to remove
# Rebase 04ec4b7..7cfa14c onto 04ec4b7 (2 commands)
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out
Type i
in order to edit, next you can add d
or drop
, before the commit you want to remove, for example :
d 7cfa14c commit to remove
Let pick
before all other commits, then type esc
in order to escape edit mode and :wq
in order to save and quit.
Now commit will be removed locally, next push update on remote branch using git push --force origin <remote_branch>
, note the --force
option, required because of rebase.
Upvotes: 0