<> See also: [[CodingStyle/Git]] == Workflow for gitorious and github == Initial checkout: {{{#!highlight sh # Fork repo on gitorious/github # Pull repo from gitorious/github # Add remote for upstream, e.g. cd Spoon-Knife git remote add upstream git://github.com/octocat/Spoon-Knife.git git fetch upstream }}} See [[https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/configuring-a-remote-for-a-fork|Configuring a remote for a fork]] When needing to merge upstream changes back into master: {{{#!highlight sh # Make sure we're in the branch we want to be in (i.e. master) git checkout master git fetch upstream # Merge from master branch of upstream into current branch git merge upstream/master }}} == Cookbook == === Delete remote branch === {{{#!highlight sh numbers=off git push origin :name-of-remote-branch # OR git push origin --delete name-of-remote-branch }}} === Reverting changes === Amend the previous commit that has not been propogated (i.e. undo it, then start a new one with the pending changes in the index): {{{ git commit --amend -a -m "New commit message" }}} Undo a commit that has not been propagated: {{{ git reset --soft HEAD^ }}} just changes the repository without changing the files you've edited on disk. However: {{{ git reset --hard HEAD^ }}} will reset both repository and revert files. Once a commit that has been propagated, there is no way to undo. However, the following will create a new commit undoing the previous commit's changes: {{{ git revert HEAD }}} To only change a commit message for a commit that has not been propogated: {{{ git commit --amend }}} === Pull with rebase instead of merge === {{{ git pull --rebase }}} Configure a branch to always do a rebase instead of a merge: {{{ git config branch.$BRANCH_NAME.rebase true }}} === Enforce .gitignore === Remove files from the repository that should have been ignored by .gitignore: {{{ git rm -r --cached . git add . git status git commit -m ".gitignore is now working" }}} === Search === {{{#!highlight sh numbers=off # Search commit log (i.e. messages) for given needle in all branches git log --all --grep='needle' # As above, but search reflog (much slower) git log -g --grep='needle' # Search contents of commits git grep -F 'needle' $(git rev-list --all) # Search contents of commit in a large repository git rev-list --all | parallel -j4 -k -X git grep --color=always -F 'needle' # Search branches (has problems) git branch -a | tr -d \* | parallel -k git grep --color=always -iF 'needle' }}} === Ignore whitespace bullshit === {{{ # Ignore white space when pulling or merging git pull -Xignore-space-change git merge -Xignore-space-change git merge -Xignore-all-space # Ignore white space changes when diff'ing git diff --ignore-space-change }}} === Rebase a branch === {{{#!highlight sh numbers=off # Without having branch-you-want-to-rebase checked out git rebase branch-you-want-to-rebase branch-to-rebase-from # With having branch-you-want-to-rebase checked out git checkout branch-you-want-to-rebase git rebase branch-to-rebase-from }}} Rebase off a branch that itself had been rebased (keep the un-rebased branch around as old-branch-to-rebase-from): {{{#!highlight sh numbers=off git rebase --onto branch-to-rebase-from old-branch-to-rebase-from branch-you-want-to-rebase }}} If you want to rebase but get updated stamps (do this independently of reordering/merging commits with `git rebase -i`: {{{#!highlight sh numbers=off git rebase --ignore-date }}} === Interactive rebase === Interactive rebase for only certain commits: {{{#!highlight sh numbers=off # Rebase only the last 2 commits git rebase -i HEAD~2 # All commits that are on feature branch, but not on trunk branch git rebase -i trunk }}} === Retroactively sign all previous commits === Uses rebase, so this will invalidate other checkouts. {{{#!highlight sh numbers=off git rebase --exec 'git commit --amend --no-edit --no-verify -S' -i --root git rebase --committer-date-is-author-date -i --root # check git log --pretty=fuller }}} Source: [[https://peterbabic.dev/blog/git-sign-previous-commits-keeping-dates/|Git sign previous commits keeping dates by Peter Babič]] == Problems == === Cannot lock ref error === {{{ error: cannot lock ref 'refs/remotes/origin/bugfix/SPL-178301-lookups-cache-results-incorrectly': }}} is caused by your local repository's copy of the origin repository's branches getting corrupted. To fix: {{{#!highlight sh numbers=off git remote prune origin }}} It will not touch any of your local branches with your edits, or anything remote, just your local repo's knowledge of remote branches. == Per-repository user settings == {{{#!highlight sh numbers=off git config user.name 'Samat K Jain' git config user.email 'nobody@example.com' }}} == Cleaning == {{{#!highlight sh numbers=off # Discard all unstaged changes in CWD git checkout -- . # Remove untracked files and directories, dry run. `-n` for dry run. git clean -df -n }}} == GPG Signing == {{{#!highlight sh # GPG sign all previous commits. Careful! git filter-branch -f --commit-filter 'git commit-tree -S "$KEY_ID"' HEAD }}} == Submodules == In the below, TARGET_DIR is something like `vendor/some-package`. === Cloning a project w/ submodules === First clone: {{{#!highlight sh numbers=off git clone --recurse-submodules … }}} If you forgot `--recurse-submodules`, can run: {{{#!highlight sh numbers=off git submodule update --init --recursive }}} === Adding submodules to a project === {{{#!highlight sh numbers=off git submodule add $REPOSITORY_URL $TARGET_DIRECTORY }}} You need to start tracking the new `.gitmodules` file (just like `.gitignore`), as well as a "file" representing the submodule. `git status` will show it as $TARGET_DIRECTORY. {{{#!highlight sh numbers=off git add .gitmodules $TARGET_DIRECTORY git commit -m 'Started tracking $REPOSITORY_URL as submodule' }}} === Update submodules === Go to each submodule, fetch, and update: {{{#!highlight sh numbers=off git submodule update --remote --recursive # git commit }}} === Remove submodules === Go to each submodule, fetch, and update: {{{#!highlight sh numbers=off git submodule rm $TARGET_DIR # git commit # Completely remove submodule (will need to be init again if you go backwards in history) to save disk space rm -Rf .git/modules/$TARGET_DIR }}} Will be done with a `git pull` automatically if `submodule.recurse` option is True. === Misc === Tell git to track a specific branch of a submodule, e.g. "master": {{{#!highlight sh numbers=off git config -f .gitmodules submodule.${TARGET_DIR}.branch master # commit .gitmodules }}} `-f` is specified so that `.gitmodule` can be updated and other people using the repository can track the same branch. Omit this if you don't care. === Settings === {{{#!highlight sh numbers=off # `--recursive` for everything but `git clone` git config --global submodule.recurse true # Show summary of submodule changes in `git status` git config --global status.submodulesummary true # Prettier diff for submodules git config --global diff.submodule log }}} == Interesting reading == [[http://tom.preston-werner.com/2009/05/19/the-git-parable.html|The Git Parable]]: Describes building a system like git, from the ground-up [[https://wikileaks.org/ciav7p1/cms/page_1179773.html|CIA's git tips & tricks]] sheet is interesting, and oddly similar to this one. == git-email-based workflow == An e-mail-based workflow is preferred by many projects, including the Linux kernel. * [[https://blog.brixit.nl/git-email-flow-versus-github-flow/|Git email flow vs Github flow]] * [[https://git-send-email.io/|git-send-email.io]] helps you get up and running w/ git-email ---- CategoryCheatSheet