Git Beginner | Warnings for potentially destructive commands
Hi,
I made an attempt at writing warning messages for the commands the beginners will be warned against, and would like to request your comments and feedback on them. I tried to keep them simple, beginner friendly and educative of the outcome of those commands.
I'd also like to ask if git rebase should be kept in the
list as it may not be bad in most cases, though I prepared a message for that anyway.
Thanks and regards,
Sidhant Sharma
The current list of graylisted commands is as follows:
$ git rebase $ git reset --hard $ git clean -f $ git gc --prune=now --aggressive $ git push -f <branch> $ git push remote [+/:]<branch> $ git branch -D
Warning messages:
$ ggit rebase
[WARNING] You are about to rebase your commits in <topic-branch> onto the $BASE_BRANCH, which will essentially replay the work done in $TOPIC_BRANCH since last merge onto $BASE_BRANCH.
For instance,
Current state:
o---o---A---B $BASE_BRANCH
\
X---Y $TOPIC_BRANCHState after rebasing:
o---o---A---B---X'---Y' $BASE_BRANCH
\
X---Y $TOPIC_BRANCHwhere X' and Y' are the commits making changes identical to those made by X and Y respectively.
Rebasing is not usually problematic except in cases when you are rebasing commits that do not exist in your repository.
$ ggit reset --hard
Resetting to <destination-commit-hash>
[WARNING] You are about to hard reset the current HEAD (master) by <n> commit(s). This will take you back to commit <destination-commit-hash>, and discard all changes make thereafter. For instance,
Current state:
o---o---A---B---C---D---E $CURRENT_BRANCH
After resetting 3 commits:
o---o---A---B $CURRENT_BRANCH
The commits C, D and E and the changes made by them will be lost. Note that if you make commits on top of B, you would have rewritten the history and would have trouble restoring it easily.
You can undo this action by resetting the HEAD to the last commit you were on, which can be seen by running git reflog. The first entry (HEAD{1}) points to the current HEAD location, second entry (HEAD{1}) points to the last position of your HEAD and so on.
If you want to reset while retaining the changes made since, use --soft instead of --hard. This will reset without discarding the changes made in the previous commits.
$ ggit push --force
Pushing changes to $REMOTE/$BRANCH
[WARNING] You are about to purge commits from the <URL of origin> master branch. For instance,
Current state:
o---o---o---A---B master on $ORIGIN_URL
\
X---Y---Z your masterState after forced push:
o---o---o---X---Y---Z master on $ORIGIN_URL and your master
Commit A and B will be gone. If other people have worked on top of A or B then they won't be able to merge their changes easily.
$ ggit push <remote> :<branch> ( ggit push <remote> --delete <branch> )
Pushing changes to $REMOTE/$BRANCH
[WARNING] You are about delete a remote branch, which will result in the loss of commits made on that branch. This may cause a problem if other people are working on the branch you are deleting, as they would not be able to push or merge their changes easily.
You can undo this by pushing the same branch to the remote with upstream flag, that is, by running:$ ggit push -u $REMOTE $BRANCH
This will work unless you have deleted the branch locally as well. In that case, you need to restore it first.
$ ggit push <remote> +<branch> ( ggit push <remote> +<basebranch>:<targetbranch> )
Pushing changes to $REMOTE/$BRANCH
[WARNING] You are attempting to push changes from $BASE_BRANCH to $TARGET_BRANCH while allowing non-fast-forward updates. This can leave unreferenced commits dangling in the origin repository.
For instance,
Current state:
o---o---A---B $REMOTE/$TARGET_BRANCH
\
X---Y---Z your $BASE_BRANCHState after forced push:
o---o---A---B (unnamed branch)
\
X---Y---Z $REMOTE/$TARGET_BRANCH and your $BASE_BRANCHCommits A and B would no longer belong to a branch with a symbolic name, and so would be unreachable. Also, people who have based their work on A or B would have to either merge or rebase after you have pushed.
If you wish to keep both the changesets (A,B and X,Y,Z), you may want to use merge or rebase.
$ ggit branch -D ( ggit branch -d -f )
Deleting branch $TARGET_BRANCH
[WARNING] You are about to force delete the $TARGET_BRANCH branch. This will cause git to delete the branch even if it has not been merged, which may result in loss of commits. Unless you are confident the branch has been merged, use -d or --delete without --force which will warn you if the branch has not been merged.
You can restore a forcefully deleted branch by running:$ git branch <branch-name> <sha1>
where <branch-name> is the name of the branch you wish to restore and <sha1> is the last commit made to the branch you want to restore. You can find this information by running git reflog.
To undo deletion of this branch, you can run:$ ggit branch $TARGET_BRANCH $LAST_COMMIT_SHA
$ ggit clean -f [<path>]
Cleaning $PATH
[WARNING] You are about to clean $PATH, which will permanently delete all files and directories present at the given path. Note that it would not be possible to revert this action, and all files not tracked by git will be list permanently. If you only wish to see what will be deleted on running ggit clean, use the -n or --dry-run option.
$ ggit gc --prune=now --aggressive (or $ ggit gc --prune=all --aggressive)
Running garbage collection
[WARNING] You are about to prune all objects, regardless of their age or reachability. The --aggressive flag tells git to aggressively optimize the repository at the cost of taking more time to complete the operation. By setting --prune option to now or all, you ask git to optimize all objects, and not just the unreachable ones. Unless the repository is quiescent, you will lose newly created objects that haven’t been anchored with the refs and end up corrupting your repository.
It is also not possible to revert the actions taken by git gc.
Accepting input:
Are you sure you want to continue? [Y/n]
Quoted 12 lines · Sidhant Sharma
$ ggit rebase[WARNING] You are about to rebase your commits in <topic-branch> onto the $BASE_BRANCH, which will essentially replay the work done in $TOPIC_BRANCH since last merge onto $BASE_BRANCH.
For instance,
Current state:o---o---A---B $BASE_BRANCH \ X---Y $TOPIC_BRANCH
Quoted 8 lines
State after rebasing:
o---o---A---B---X'---Y' $BASE_BRANCH \ X---Y $TOPIC_BRANCHwhere X' and Y' are the commits making changes identical to those made by X and Y respectively.
The topology may be correct, but the branch labels are both wrong, no? The tip of the base branch will stay at B, and the tip of the topic will point at Y'.
Quoted 2 lines
Rebasing is not usually problematic except in cases when you are rebasing commits that do not exist in your repository.
This cannot be correct, as you fundamentally cannot work on (not limited to rebasing) commits that do not exist in your repository.
Quoted 4 lines
$ ggit reset --hardResetting to <destination-commit-hash>
[WARNING] You are about to hard reset the current HEAD (master) by <n> commit(s).
If I were on B and did "git reset --hard Y", i.e.
o---o---A---B $CURRENT_BRANCH
\
X---Y $CURRENT_BRANCH_AFTER_RESETTINGdoes the phrasing "about to reset by <n> commit(s):" make any sense?
Quoted 9 lines
This will take you back to commit <destination-commit-hash>, and discard all changes make thereafter. For instance,
Current state:o---o---A---B---C---D---E $CURRENT_BRANCHAfter resetting 3 commits:
o---o---A---B $CURRENT_BRANCH
The above two examples make me wonder if these should be static text. "ggit rebase" and "ggit reset" have full information of the concrete branch names, commit object names and the actual topology of the history, so it should be able to give a description more tailored to the user's situation. Instead of giving a fictional drawing with "For instance, Current state:", it should be able to draw the actual before-and-after picture based on where the end-user actually is. I see _some_ attempts (e.g. with "<n>", mention of "(master)" and $BASE_BRANCH, you may have meant that they will be replaced with actual values), but I suspect that telling some truth (i.e. use of the real branch names) while showing pictures that do not match the reality (i.e. if the topology and the description are done as fixed text) would only confuse the users.
Quoted 5 lines · Junio C Hamano
The above two examples make me wonder if these should be static text. "ggit rebase" and "ggit reset" have full information of the concrete branch names, commit object names and the actual topology of the history, so it should be able to give a description more tailored to the user's situation. Instead of giving a fictional
Quoted 8 lines
drawing with "For instance, Current state:", it should be able to draw the actual before-and-after picture based on where the end-user actually is. I see _some_ attempts (e.g. with "<n>", mention of "(master)" and $BASE_BRANCH, you may have meant that they will be replaced with actual values), but I suspect that telling some truth (i.e. use of the real branch names) while showing pictures that do not match the reality (i.e. if the topology and the description are done as fixed text) would only confuse the users.
If possible, I would suggest aiming for generating the actual topology that the user is seeing, customized so that it gives relevenat information, rather than static examples. It may be that it is not possible or the effort is too large for such a project. If the latter is the case, then using only static text is better than trying to use some but not all the available information, as Junio points out above.
Regards,
Jake
Quoted 3 lines · Jacob Keller
If possible, I would suggest aiming for generating the actual topology that the user is seeing, customized so that it gives relevenat information, rather than static examples.
Using the real topology in a useful way is actually pretty hard. It's quite easy to throw the output of "git log --graph --oneline ..." to the user, but as soon as the rebase deals with more than a handfull of commits, we'd want to simplify the history to show something understandable to the user (which by definition should be a beginner if he uses ggit), like replacing long sequences of commits with "..." or so. That is hard to get right.
Signature · 2 lines
Matthieu Moy http://www-verimag.imag.fr/~moy/
Quoted 6 lines · Matthieu Moy
Quoted 3 lines · Jacob Keller
If possible, I would suggest aiming for generating the actual topology that the user is seeing, customized so that it gives relevenat information, rather than static examples.
Quoted 8 lines
Using the real topology in a useful way is actually pretty hard. It's quite easy to throw the output of "git log --graph --oneline ..." to the user, but as soon as the rebase deals with more than a handfull of commits, we'd want to simplify the history to show something understandable to the user (which by definition should be a beginner if he uses ggit), like replacing long sequences of commits with "..." or so. That is hard to get right.
Yes, in which case we should go Junio's route of not using anything from the real topology.
Thanks,
Jake
Quoted 20 lines · Junio C Hamano
Quoted 18 lines · Sidhant Sharma
$ ggit rebase[WARNING] You are about to rebase your commits in <topic-branch> onto the $BASE_BRANCH, which will essentially replay the work done in $TOPIC_BRANCH since last merge onto $BASE_BRANCH.
For instance,
Current state:o---o---A---B $BASE_BRANCH \ X---Y $TOPIC_BRANCHState after rebasing:
o---o---A---B---X'---Y' $BASE_BRANCH \ X---Y $TOPIC_BRANCH
Quoted 5 lines
Quoted 2 lines
where X' and Y' are the commits making changes identical to those made by X and Y respectively.
The topology may be correct, but the branch labels are both wrong, no? The tip of the base branch will stay at B, and the tip of the topic will point at Y'.
Thanks for pointing that out, will correct that.
Quoted 5 lines
Quoted 2 lines
Rebasing is not usually problematic except in cases when you are rebasing commits that do not exist in your repository.
This cannot be correct, as you fundamentally cannot work on (not limited to rebasing) commits that do not exist in your repository.
Actually I meant to refer to the commits that exist outside the local repo (eg. on the remote), like it says in the 'The Perils of Rebasing' section of the Git Branching and Rebasing documentation [1]. I'll rephrase it to make it clearer.
Quoted 34 lines
Quoted 4 lines
$ ggit reset --hardResetting to <destination-commit-hash>
[WARNING] You are about to hard reset the current HEAD (master) by <n> commit(s).If I were on B and did "git reset --hard Y", i.e.
o---o---A---B $CURRENT_BRANCH \ X---Y $CURRENT_BRANCH_AFTER_RESETTINGdoes the phrasing "about to reset by <n> commit(s):" make any sense?
Quoted 9 lines
This will take you back to commit <destination-commit-hash>, and discard all changes make thereafter. For instance,
Current state:o---o---A---B---C---D---E $CURRENT_BRANCHAfter resetting 3 commits:
o---o---A---B $CURRENT_BRANCHThe above two examples make me wonder if these should be static text. "ggit rebase" and "ggit reset" have full information of the concrete branch names, commit object names and the actual topology of the history, so it should be able to give a description more tailored to the user's situation. Instead of giving a fictional drawing with "For instance, Current state:", it should be able to draw the actual before-and-after picture based on where the end-user actually is. I see _some_ attempts (e.g. with "<n>", mention of "(master)" and $BASE_BRANCH, you may have meant that they will be replaced with actual values), but I suspect that telling some truth (i.e. use of the real branch names) while showing pictures that do not match the reality (i.e. if the topology and the description are done as fixed text) would only confuse the users.
Quoted 15 lines · Jacob Keller
Quoted 5 lines · Matthieu Moy
Quoted 3 lines · Jacob Keller
If possible, I would suggest aiming for generating the actual topology that the user is seeing, customized so that it gives relevenat information, rather than static examples.
Quoted 8 lines
Using the real topology in a useful way is actually pretty hard. It's quite easy to throw the output of "git log --graph --oneline ..." to the user, but as soon as the rebase deals with more than a handfull of commits, we'd want to simplify the history to show something understandable to the user (which by definition should be a beginner if he uses ggit), like replacing long sequences of commits with "..." or so. That is hard to get right.
Quoted 3 lines
Yes, in which case we should go Junio's route of not using anything from the real topology.
I now understand why using the actual names with a hypothetical topology would only confuse the user. I'll update the diagrams with hypothetical names.
Also, other than these commands, what others should I include in the list? I think we may also have warnings for the following:
* git checkout -- <path>
* git rm --cached
* git stash drop [<stash>]
* git stash clear
* All plumbing commands?
Comments?
Thanks and regards,
Sidhant Sharma
[1]: https://git-scm.com/book/en/v2/Git-Branching-Rebasing#The-Perils-of-Rebasing
[RFC/GSOC] Git Beginner | Warnings for potentially destructive commands (v2)
In reply to Sidhant SharmaUpdated the warning messages, and added warnings for two more commands (checkout -- and stash clear). The list is now:
$ git rebase $ git reset --hard $ git clean -f $ git gc --prune=now --aggressive $ git push -f <branch> $ git push remote [+/:]<branch> $ git branch -D $ git checkout [-p] [<tree-ish>] -- <path> $ git stash clear
Looking forward to your comments.
Thanks and regards,
Sidhant Sharma
---
$ ggit rebase
[WARNING] You are about to rebase your commits in $TOPIC_BRANCH onto the $BASE_BRANCH, which will essentially replay the work done in $TOPIC_BRANCH since last merge onto $BASE_BRANCH.
For instance, assume the following history exists and the current branch is master:
o---o---A---B master
\
X---Y topicAfter rebasing, the history would be:
o---o---A---B master
\
X'---Y' topicwhere X' and Y' are the commits making changes identical to those made by X and Y respectively.
Rebasing is not usually problematic except in cases when you are rebasing commits that exist outside your repository (such as on a remote or on someone else's computer).
$ ggit reset --hard
Resetting to <destination-commit-hash>
[WARNING] You are about to hard reset the current HEAD ($CURRENT_BRANCH) to a previous commit in history, possibly in another branch. This will discard all changes make thereafter.
For instance, assume the following history exists and the current branch is master:
o---o---A---B---C---D---E master
After resetting 3 commits:
o---o---A---B master
The commits C, D and E and the changes made by them will be lost. Note that if you make commits on top of B, you would have rewritten the history and would have trouble restoring it easily.
You can undo this action by resetting the HEAD to the last commit you were on, which can be seen by running git reflog. The first entry (HEAD{1}) points to the current HEAD location, second entry (HEAD{1}) points to the last position of your HEAD and so on.
If you want to reset while retaining the changes made since, use --soft instead of --hard. This will reset without discarding the changes made in the previous commits.
$ ggit push --force
Pushing changes to $REMOTE/$BRANCH
[WARNING] You are about to purge commits from the $REMOTE/$BRANCH branch and overwrite it's history to match yours.
For instance, assume the following history exists where 'origin' is a configured remote and the current branch is master:
o---o---o---A---B origin/master
\
X---Y---Z your masterAfter force push:
o---o---o---X---Y---Z origin/master and your master
Commit A and B will be gone. If other people have worked on top of A or B then they won't be able to merge their changes easily.
To revert this, you would have to force push from a computer that has not yet pulled the changes you pushed and still has commits A and B as they were in origin/master previously.
$ ggit push <remote> :<branch> ( ggit push <remote> --delete <branch> )
Pushing changes to $REMOTE/$BRANCH
[WARNING] You are about delete a remote branch, which will result in the loss of commits made on that branch. This may cause a problem if other people are working on the branch you are deleting, as they would not be able to push or merge their changes easily.
You can undo this by pushing the same branch to the remote with upstream flag, that is, by running:$ ggit push -u $REMOTE $BRANCH
This will work unless you have deleted the branch locally as well. In that case, you need to restore it first.
$ ggit push <remote> +<branch> ( or ggit push <remote> +<basebranch>:<targetbranch> )
Pushing changes to $REMOTE/$BRANCH
[WARNING] You are attempting to push changes from $BASE_BRANCH to $TARGET_BRANCH while allowing non-fast-forward updates. This can leave unreferenced commits dangling in the origin repository.
For instance, assume the following history exists where 'origin' is a configured remote and the current branch is master:
o---o---A---B origin/master
\
X---Y---Z your masterState after forced push:
o---o---A---B (unnamed branch)
\
X---Y---Z origin/master and your masterCommits A and B would no longer belong to a branch with a symbolic name, and so would be unreachable. Also, people who have based their work on A or B would have to either merge or rebase after you have pushed.
If you wish to keep both the changesets (A,B and X,Y,Z), you may want to use merge or rebase.
$ ggit branch -D ( or ggit branch -d -f )
Deleting branch $TARGET_BRANCH
[WARNING] You are about to force delete the $TARGET_BRANCH branch. This will cause git to delete the branch even if it has not been merged, which may result in loss of commits. Unless you are confident the branch has been merged, use -d or --delete without --force which will warn you if the branch has not been merged.
You can restore a forcefully deleted branch by running:$ git branch <branch-name> <sha1>
where <branch-name> is the name of the branch you wish to restore and <sha1> is the last commit made to the branch you want to restore. You can find this information by running git reflog.
To undo deletion of this branch, you can run:$ ggit branch $TARGET_BRANCH $LAST_COMMIT_SHA
$ ggit clean -f [<path>]
Cleaning <path>
[WARNING] You are about to clean <path>, which will delete all files and directories at the given path which are not tracked by git. Note that it would not be possible to revert this action, and all files not in present in the index will be lost permanently.
If you only wish to see what will be deleted on running ggit clean, use the -n or --dry-run option.
$ ggit gc --prune=now --aggressive ( ggit gc --prune=all --aggressive)
Running garbage collection
[WARNING] You are about to prune all objects, regardless of their age or reachability. The --aggressive flag tells git to aggressively optimize the repository at the cost of taking more time to complete the operation. By setting --prune option to now or all, you ask git to optimize all objects, and not just the unreachable ones. Unless the repository is quiescent, you will lose newly created objects that haven’t been anchored with the refs and end up corrupting your repository.
It is also not possible to revert the actions taken by git gc.
$ ggit checkout [-p] [<tree-ish>] -- <path>
Discarding changes from working tree
[WARNING] You are about to permanently discard the unstaged changes present in <path>. This will bring <path> to its state as stored in the index, or to the state at <tree-ish>.
If you wish to see what changes will be discarded and/or choose which changes are will be discarded, use the -p or --patch flag.
The actions made by this command cannot be undone.
$ ggit rm [--cached] <file>
Removing file(s): <file>
[WARNING] You are about to remove files/directories from the git index. This will cause git to stop tracking them, as well as delete them from the local filesystem. Using the --cached flag will only remove them from the index and not the filesystem.
If you only wish to see which files will be removed, use the -n or --dry-run flag.
You can revert the effects of this command by either restoring the files manually and running ggit add <file> or by running ggit reset --hard.
$ ggit stash clear
Dropping all stashed states
[WARNING] You are about to remove all the stashed states. Note that those states will then be subject to pruning, and may be impossible to recover. To view all stashes, use ggit stash list. To selectively drop stashes, use ggit stash drop <stash>.
Accepting input
Are you sure you want to continue? [Y/n]
Re: [RFC/GSOC] Git Beginner | Warnings for potentially destructive commands (v2)
Quoted 8 lines · Sidhant Sharma
$ ggit push --forcePushing changes to $REMOTE/$BRANCH
[WARNING] You are about to purge commits from the $REMOTE/$BRANCH branch and overwrite it's history to match yours.
For instance, assume the following history exists where 'origin' is a configured remote and the current branch is master:
Quoted 7 lines
o---o---o---A---B origin/master \ X---Y---Z your masterAfter force push:
o---o---o---X---Y---Z origin/master and your master
It should be:
o---o---X---Y---Z origin/master and your master
(The last 'o' commit should have been overwritten by the force push)
Thanks,
Rémi