Git usage

From wikinotes

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

files

## 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 files

git stash
git stash pop
git stash list

log

## 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

status

## status
git status
git diff

clone

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 checkout

submodules

# 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 sync

multiple 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.patch

Squash 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.patch

search

# ==========
# 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 tags

Creating 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.1

show

git show master:/path/to/file.rb  # show file without changing branches
git show 1.1.1                    # show tag 1.1.1

commits

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

diff

difftool

Pro tip, you can use vimdiff as a diff tool

git difftool --tool vimdiff2

Within vim you can use:

  • 1,20diffput to put everything from line 1-20 into the other buffer
  • do pull block from other side
  • dp push block to other side

Advanced

filter-branch

WARNING:

git's documentation says not to use this command.

use git-filter-repo instead.

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

rebase

See https://git-rebase.io/

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 commits

to 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 commits

blame

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-10

revert

# before push

# after push (revert branch)
git revert HEAD~5..HEAD  # revert everything from last 5x commits in single commit

Workflows

merge conflict

git merge ${BRANCH}
git diff ORIG_HEAD MERGE_HEAD file.txt  # show diff of conflict