Problem: Can't push because remote and local have diverged.
Solution: Fetch, then compare local vs remote to check if anything bad is likely to happen when we merge:
>git fetch >git status On branch master480a7506288d93cfdb9d84dc5ecdb389bb906e09 Your branch and 'origin/master' have diverged, and have 1 and 1 different commit each, respectively. >git log HEAD..origin/master
This will show the remote commit(s) that don't exist locally.
You can see the actual differences using:
>git diff --color-words master origin/master
>git difftool master origin/master
If it looks safe to merge then:
>git merge origin/master
Better yet, switch to new branch, and merge there first just to be sure:
>git checkout -b merging >git merge origin/master
Problem: "Bad things" happened.
Solution: You can pretty much always get back to a previous state using the
# work, test, stage, commit, do "bad things" # first commit or stash anything that you want to keep - if it is committed it is really hard to lose it # back up the repo (make a copy of the entire thing) - it is really hard to lose stuff if you have a backup # now find the commit ID just prior to the 'bad things happened' git reflog # hard reset back to the good version git reset --hard <commit ID>
Note — this is only really a good idea if you are just trying to revert a recent mistake that has not been pushed, or revert a bad merge. If you are resetting work that has been pushed to the remote then you will effectively be rewriting history which is only going to make things worse particularly if other people are using the same repository. A revert commit is the better way to deal with things in this situation.
If the files still exist locally and we only care about getting them out of the repo, and not about removing all traces from the history:
git rm -rf --cached --dry-run -- <rubbish> git rm -rf --cached -- <rubbish> # add <rubbish> to .gitignore so it doesn't make it back into the repo echo <rubbish> >>! .gitignore
If files have been removed locally via other means then stage and commit the removal using:
git stage --all <path> git status
If you are getting
413 / Payload Too Large errors as a result of committing big files then you need to remove all traces of the offending files from the history. This is effectively rewriting history, so warn team members prior to doing this, and let them know that they will likely need to clone a new copy:
git log --pretty=format: --name-status | cut -f2- | sort -u
This will show all files that have ever existed in the repository. Once you identify the offending files you can use the BFG repo cleaner to remove all traces of them from the history.
This tool can also be used to remove sensitive files from the entire history, or even search and replace sensitive text.
# stage and commit everything first, and get repo into a clean state git stage src # stage anything important git commit git status # check if repo is clean and everything important is committed git reset --hard # reset any rubbish that needs it to get into a clean state # it is recommended that you start with a bare mirrored version of the repo - at bare minimum it means that you won't be borking your original repo if things go wrong cd .. git clone --mirror busted_repo mirrored_repo # clean repo java -jar bfg.jar --delete-folders build,.gradle --delete-files .DS_Store --no-blob-protection mirrored_repo # garbage collect the repo cd mirrored_repo git reflog expire --expire=now --all && git gc --prune=now --aggressive # push to remote git push # clone new repo from remote git clone https://... fixed_repo
fixed_repo is good, then get rid of the
# find the ID for the commit that you want to undo git log # check that you definitely have the right commit git show <commit ID> # revert that commit git revert <commit ID>
This effectively adds another commit that undoes the reverted commit. This is safe to push to a shared repo.
# kill local branch git branch -d <branch> # kill local tag git tag -d <tag> # kill remote branch/tag git push origin --delete <branch / tag>
Use topic branches to keep your master clean while you are working on stuff. You can also squash a branch with a messy commit history into something that is a bit more acceptable before merging into master.
# sync origin/master from remote git fetch origin master # verify which branch we are on git branch # if we are not on the topic branch then check it out git checkout topic/bob # rebase the branch on master to make sure we are up to date git rebase master git rebase origin/master # work, test, stage, commit ... # push the topic git push origin topic/bob
When topic is complete, merge back to master.
If it is a simple history, or you want to preserve the entire branch history in the master:
git checkout master git merge topic/bob
If the history is screwy or you want to collapse it into larger commits for sharing.
# rebase master to make sure branch is current git rebase master # view history to find commit IDs git log --reverse # interactively rebase the commits into something legible git rebase -i <commit ID> # merge back onto master git checkout master git merge topic/bob
You will usually select the commit before the one you want to keep, and then
pick the first line, and
squash the rest.
git branch --no-merged
Useful when you don't want to commit but need to do something potentially destructive like switch branches. Also useful if you did a bunch of work on the wrong branch and want to checkout the right right branch before committing.
Add a stash:
git stash push
If you want to add a message (if you expect to keep the stash around) then use:
git stash push -m "message"
You can view stashes using:
git stash list
You can show the contents of a stash using:
git stash show
To apply a stash:
git stash pop
This will apply and drop the most recent stash. If a merge conflict happens then the stash will not be dropped.
If you want to keep the stash around after applying it then use:
git stash apply
If you want to drop a stash:
git stash drop
There currently isn't a
git merge --dry-run, but the following is a reasonable approximation:
git fetch git merge --no-ff --no-commit origin/master # merge result will be reported # we are left in a partial merge state, so abort git merge --abort
FF merges are not able to be stopped, so we disable FF merge with
--no-commit will stop the merge result from being committed.
>git remote add origin https://... >git push -u origin master
origin for remote name if you don't want to use origin.