Git usage
basics
branches
## branches git branch f-mynewfeature ## create branch git checkout f-mynewfeature ## checkout branch git checkout HEAD --force ## checkout branch, git checkout $(git merge-base @ origin/HEAD) # checkout commit before branch start git remote show origin | grep 'HEAD branch' | cut -wf4 # get name of master/main branchfiles
## files git add path/to/file ## add file to branch's stage git add --all :/ ## add all changed files to branch's stage git checkout HEAD path/to/file ## checkout a file from an older commit git checkout f-mybranch path/to/file ## checkout a file from an older commit git commit ## commit current stage to branch git commit --amend ## add current changes to last commit git rm path/to/file ## remove file from git git mv path/to/file path/to/other ## move a file (history continues to track file)stage
git checkout HEAD path/to/file git checkout f-mynewfeature path/to/file git add path/to/file git add --all :/ git commit git push git diff HEAD..HEAD~2 path/to/file git clean -fd # remove untracked filesstash
git stash # what to stash -au `# all changes` -S `# only-staged changes` -k `# stash, but keep index` # optional message -m 'msg' `# description of stash` git stash list git stash pop git stash drop 0log
## log git log git log --oneline git log --abbrev-commit # use short commit hashes git log --grep 'Deprecated' # search commit messages for 'Deprecated' git log -G 'def foo' # search commit diffs for changes involving 'def foo' git log /path/to/file git log -L${start_ln},${end_ln}:${filepath} # view log for all changes to this selection of lines git log -L${function}:${filepath} git log --all --full-history -- ${filepath} # view log for a deleted file git log --format="%H" --full-history -- ${filepath} # list commits for a deleted file git log -p --full-history --no-merges -- ${filepath} # list commits with diffs for a deleted filestatus
## status git status git diffclone
git clone https://github/blah.git git clone ssh://git@server:8888/path/to/repo git clone ssh://server:/path/to/repo ## NOTE: if you have configured your ~/.ssh/config with all info, you only need to specify machine git clone file:////home/you/src git clone --depth=1 https://github.com/user/blah # clone only latest checkoutpush
git push $remote $commit:$remote_branch # push specific commit to branch git push -f -u origin local_branch:remote_branch # replace remote-branch with a different local-branchpull
git pull # pull changes from branch # set local head to match a rebased remote-branch git fetch git reset --hard origin/master # convert shallow clone to full clone git fetch --unshallow # this git fetch --depth=10000 # or thissubmodules
# Adding a submodule to a project cd ../my_git_project git submodule add ssh://gitbox:/home/git/_python/supercli git submodule update --init # manage submodules git submodule update # update to recorded revision git submodule deinit # un-clone dir # remove submodule vim .gitmodules # remove your submodule git config --edit # remove your submodule git rm --cached your/submodule # remove your submodule's path git submodule syncmultiple urls for a submodule:
#### .git/modules/..submodule_name.../config [remote "origin"] url = ssh://user@box1:2222/path/to/repo url = ssh://user@box2:2222/path/to/other/repo ####patches
Create patch from unstaged changes
# in a repo with unstaged changes cd /path/to/projectroot git diff --binary > description.patch cd /path/to/other/projectroot git apply description.patchSquash all differences between branches into a patch
git merge needs_changes has_changes # VERY IMPORTANT! git checkout -b deleteme has_changes git reset --soft needs_changes git diff --binary HEAD > ~/description.patch git checkout -b recv_patch needs_changes git apply ~/description.patchsearch
# ========== # Searching # ========== git log \ -p \ # print diff -m \ # include merge commit diffs -G '\$\(document\)' \ # regex commit+diff --abbrev-commit \ # short commit hashes --name-only \ # when used with `-p`, only filenames are shown instead of full diffs git log -G "regex" # search commits, whose text, or diff contains this regex git log --abbrev-commit -p -m -G "regex" # seearch log, show diffs, (even commit diffs), matching regex git log --abbrev-commit -p -m -- file.txt # show commits/diffs for all commits related to file git rev-list --all | xargs git grep <regex> # search all changesets for keyword git grep <regex> $(git rev-list --max-count=50) # search last 50 changesets # show all commits involving file gitk --all --first-parent --remotes --reflog --author-date-order -- filename git log --all --first-parent --remotes --reflog --author-date-order -- filename # show all commits involving file, including diffs for merge commits for f in `git rev-list --all --first-parent --remotes --reflog --author-date-order -- your_filename.rb | tr '\n' ' '`; do echo "========\n$f\n========"; git show -m --color=always $f | cat ; done | less -Ri # ============ # Show Changes # ============ git show aaaaaaaa # show commit message/changes (except merges) git show -U 30 aaaaaaaa # show commit message/changes and 30 lines surrounding git diff aaaaaaaa^! # show changes (always works)tags
You can tag releases of your git repository with versions so that they are easy to revert.
Listing Tags
git tag # list all tags git tag -l '1.*' # list matching tagsCreating Tags
# create lightweight tag 1.1.1 git tag 1.1.1 # create annotated tag # (a real git commit, can be GPG signed, has commit message) git tag \ -a 1.1.1 \ -m "my released 1.1.1"Tag Info
git show 1.1.1 # print info about tag 1.1.1show
git show master:/path/to/file.rb # show file without changing branches git show 1.1.1 # show tag 1.1.1commits
git diff ${COMMIT}~ ${COMMIT} # show changes introduced by a commit (since commit before it) git rev-list -1 --before='2021-08-16T18:00:00' master # find commit(s) before a specific datetime git cherry-pick ${oldest_commit}^..${newest_commit} # cherry-pick a range of commitsdiff
# print all files changed on branch { git diff main... --name-only \ && git diff --staged --name-only \ && git diff HEAD --name-only; }difftool
Pro tip, you can use vimdiff as a diff tool
git difftool --tool vimdiff2Within vim you can use:
1,20diffput
to put everything from line 1-20 into the other bufferdo
pull block from other sidedp
push block to other side
Advanced
filter-branch
Remove target file anywhere it is found in entire git history.
git filter-branch \ -f \ --index-filter 'git rm --cached --ignore-unmatch *.sql' git filter-branch \ -d /your/temp/workdir `# temporary working dir for filtered branch` \ --prune-empty `# delete commits that are now empty as result of oper` \ --index-filter `# executes command on every commit` \ "git rm --cached -f --ignore-unmatch oops.iso" \ --tag-name-filter cat `# rename tags (references to files) so history is consistent` \ -- --all `# run this on every commit in the entire git history`See
- https://stackoverflow.com/questions/2047465/how-can-i-delete-a-file-from-a-git-repository
- https://stackoverflow.com/questions/2100907/how-to-remove-delete-a-large-file-from-commit-history-in-git-repository
rebase
git log --nameonly origin/master..HEAD # count number of log entries. git rebase -i HEAD~3 # rebase last 3 commits (1-indexed)
- editor will open. Change letter in front of each commit to desired operation
- change commit, then
git rebase --continue
to continue working through commitsto split up a commit
git rebase -i HEAD~1 # 'e' in front of commit git reset HEAD^ # now git add, or use git-gui to add lines you want in your new commitsIf you've accidentally dropped commits, find them again with
git reflog
git reflog # find commit by checking it out, search code git checkout my-branch git reset --hard 1258f0d0aae # set your branch to point to this commitblame
git blame helps you find who wrote a section of a file, so you can ask them for details about how it works.
git blame filename # find last author to modify any line in file git blame filename -L 0,10 # find last author to modify lines 0-10revert
# before push # after push (revert branch) git revert HEAD~5..HEAD # revert everything from last 5x commits in single commitbisect
Start a git bisect, and indicate range
git bisect start git bisect bad [commit] # indicate known bad commit (defaults to current) git bisect good [commit] # indicate known good commit (defaults to current)Git will then continuously check midpoints and you indicate if the commit is good or bad
git bisect good # mark as working git bisect bad # mark as not workingEventually, it will inform you the branch where the regression was introduced.
Usegit show
to see the changes introduced.
If your regression is scriptable, you can have git automatically perform the checks (using the exit-code to identify good commits)# git bisect start... etc git bisect run testme.sh
Workflows
Tracking commits ahead/behind
git log --oneline @{u}.. | wc -l # num commits ahead git log --oneline ..@{u} | wc -l # num commits behindReplace History With another Repo
Say you want to publish a private repo to github once you feel good about it.
# clone the git repo whose history you want to replace git remote add home git://foo.com/repo git pull home master --allow-unrelated-histories git update-ref -d HEAD git checkout HEAD --force git clean -fdmerge conflict
git merge ${BRANCH} git diff ORIG_HEAD MERGE_HEAD file.txt # show diff of conflictsquash commit history (rolling N commits)
Untested, but considering for some high traffic repos keeping a rolling log of say 15 commits.
See https://stackoverflow.com/questions/1657017/how-to-squash-all-git-commits-into-one
# set head at beginning of history, retaining changes from all commits git reset $(git commit-tree HEAD^{tree} -m "A new start")