Git usage: Difference between revisions
(→rebase) |
|||
(19 intermediate revisions by the same user not shown) | |||
Line 8: | Line 8: | ||
git checkout HEAD --force ## checkout branch, | git checkout HEAD --force ## checkout branch, | ||
git checkout $(git merge-base @ origin/HEAD) # checkout commit before branch start | 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 branch | |||
</syntaxhighlight> | </syntaxhighlight> | ||
Line 37: | Line 38: | ||
git clean -fd # remove untracked files | git clean -fd # remove untracked files | ||
</syntaxhighlight> | |||
== stash == | |||
<blockquote> | |||
<syntaxhighlight lang="bash"> | |||
git stash | 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 pop | ||
git stash | git stash drop 0 | ||
</syntaxhighlight> | </syntaxhighlight> | ||
</blockquote><!-- stash --> | |||
== log == | == log == | ||
Line 48: | Line 63: | ||
git log --oneline | git log --oneline | ||
git log --abbrev-commit # use short commit hashes | git log --abbrev-commit # use short commit hashes | ||
git log --grep 'Deprecated' # search commit messages | 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 /path/to/file | ||
git log -L${start_ln},${end_ln}:${filepath} # view log for all changes to this selection of lines | 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 -L${function}:${filepath} | ||
git log --all --full-history -- ${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 file | |||
</syntaxhighlight> | </syntaxhighlight> | ||
Line 69: | Line 87: | ||
git clone file:////home/you/src | git clone file:////home/you/src | ||
git clone --depth=1 https://github.com/user/blah # clone only latest checkout | git clone --depth=1 https://github.com/user/blah # clone only latest checkout | ||
</syntaxhighlight> | |||
== push == | |||
<syntaxhighlight lang="bash"> | |||
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-branch | |||
</syntaxhighlight> | |||
== pull == | |||
<syntaxhighlight lang="bash"> | |||
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 this | |||
</syntaxhighlight> | </syntaxhighlight> | ||
== submodules == | == submodules == | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
# Adding a submodule to a project | |||
cd ../my_git_project | cd ../my_git_project | ||
git submodule add ssh://gitbox:/home/git/_python/supercli | git submodule add ssh://gitbox:/home/git/_python/supercli | ||
git submodule update --init | git submodule update --init | ||
## | # manage submodules | ||
git submodule deinit | git submodule update # update to recorded revision | ||
git submodule deinit # un-clone dir | |||
# | |||
# remove submodule | |||
git | vim .gitmodules # remove your submodule | ||
git config --edit # remove your submodule | |||
git | git rm --cached your/submodule # remove your submodule's path | ||
# | |||
git submodule sync | git submodule sync | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Line 199: | Line 227: | ||
git diff ${COMMIT}~ ${COMMIT} # show changes introduced by a commit (since commit before it) | 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 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 commits | |||
</source> | </source> | ||
== diff == | == diff == | ||
<source lang="bash"> | |||
# print all files changed on branch | |||
{ | |||
git diff main... --name-only \ | |||
&& git diff --staged --name-only \ | |||
&& git diff HEAD --name-only; | |||
} | |||
</source> | |||
=== difftool === | === difftool === | ||
Pro tip, you can use vimdiff as a diff tool | Pro tip, you can use vimdiff as a diff tool | ||
Line 260: | Line 299: | ||
# now git add, or use git-gui to add lines you want in your new commits | # now git add, or use git-gui to add lines you want in your new commits | ||
</source> | |||
If you've accidentally dropped commits, find them again with <code>git reflog</code> | |||
<source lang="bash"> | |||
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 commit | |||
</source> | </source> | ||
Line 278: | Line 324: | ||
git revert HEAD~5..HEAD # revert everything from last 5x commits in single commit | git revert HEAD~5..HEAD # revert everything from last 5x commits in single commit | ||
</source> | </source> | ||
== bisect == | |||
Start a git bisect, and indicate range | |||
<syntaxhighlight lang="bash"> | |||
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) | |||
</syntaxhighlight> | |||
Git will then continuously check midpoints and you indicate if the commit is good or bad | |||
<syntaxhighlight lang="bash"> | |||
git bisect good # mark as working | |||
git bisect bad # mark as not working | |||
</syntaxhighlight> | |||
Eventually, it will inform you the branch where the regression was introduced.<br> | |||
Use <code>git show</code> 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) | |||
<syntaxhighlight lang="bash"> | |||
# git bisect start... etc | |||
git bisect run testme.sh | |||
</syntaxhighlight> | |||
</blockquote><!-- Advanced --> | </blockquote><!-- Advanced --> | ||
= Workflows = | = Workflows = | ||
<blockquote> | <blockquote> | ||
== Tracking commits ahead/behind == | |||
<blockquote> | |||
<syntaxhighlight lang="bash"> | |||
git log --oneline @{u}.. | wc -l # num commits ahead | |||
git log --oneline ..@{u} | wc -l # num commits behind | |||
</syntaxhighlight> | |||
</blockquote><!-- Tracking commits ahead/behind --> | |||
== Replace History With another Repo == | |||
<blockquote> | |||
Say you want to publish a private repo to github once you feel good about it. | |||
<syntaxhighlight lang="bash"> | |||
# 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 -fd | |||
</syntaxhighlight> | |||
</blockquote><!-- Replace History With another Repo --> | |||
== merge conflict == | == merge conflict == | ||
<blockquote> | <blockquote> | ||
Line 289: | Line 380: | ||
</source> | </source> | ||
</blockquote><!-- merge conflict --> | </blockquote><!-- merge conflict --> | ||
== squash commit history (rolling N commits) == | |||
<blockquote> | |||
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 | |||
<syntaxhighlight lang="bash"> | |||
# set head at beginning of history, retaining changes from all commits | |||
git reset $(git commit-tree HEAD^{tree} -m "A new start") | |||
</syntaxhighlight> | |||
</blockquote><!-- squash history --> | |||
</blockquote><!-- Workflows --> | </blockquote><!-- Workflows --> |
Latest revision as of 16:51, 23 January 2024
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")