template: title
.mega-octicon.octicon-circuit-board[]
Patrick McKenna
template: content
A selection of:
-
how to's
-
general advice for working with Git
template: content
Assumptions:
-
You're comfortable with command line Git
-
You understand the basics of Git's internals
-
You're familiar with the basics of shell scripting
template: content
Assumptions:
-
You're comfortable with command line Git
-
You understand the basics of Git's internals
-
You're familiar with the basics of shell scripting
Note: examples checked on Git 2.11.0
template: section
.mega-octicon.octicon-circuit-board[]
template: content
For example:
What commits were added to <branch>
by my last pull
?
--
git-show-new <branch>
template: content
First, we need accept a <branch>
arg
--
... and provide a sensible default if none supplied
--
#!/bin/bash
if [ $1 ]; then
branch="$1"
else
branch="HEAD"
fi
# ...
template: content
Next: generate the list of new commits
--
Use git rev-list
--
It generates and traverses commit graphs
template: content
General syntax for commits reachable from A
but not B
:
--
git rev-list A ^B
--
(A
, B
are commit-ish)
template: content
General syntax for commits reachable from A
but not B
:
git rev-list A ^B
Could use alternate sytax...
git rev-list B..A
template: content
git rev-list A ^B
--
In our case:
A
is the commit to which<branch>
currently points
template: content
git rev-list A ^B
In our case:
-
A
is the commit to which<branch>
currently points -
B
is the commit<branch>
pointed to before our latestpull
template: content
How do we specify the commit <branch>
used to point to?
--
Using @{<n>}
syntax:
--
<branch>@{<n>}
--
Gets the nth prior value of <branch>
template: content
Hence:
git rev-list <branch> ^<branch>@{1}
template: content
A final version might look like this:
#!/bin/bash
if [ $1 ]; then
branch="$1"
else
branch="HEAD"
fi
printf "\n%s%s\n\n" $(git rev-list $branch ^$branch@{1} | wc -l) \
" commits were added by your last update to $branch:"
git --no-pager log $branch ^$branch@{1} --oneline
template: content
--
Be careful with Git's "convenience" syntax, e.g.@
--
BASE=$(git merge-base @ @{u} 2>/dev/null || echo "none")
--
What are those @
symbols?!
--
Those are the Unicode code points for the "the Git command line was getting too comprehensible" character in Swahili
template: content
BASE=$(git merge-base @ @{u} 2>/dev/null || echo "none")
--
- the
@
is short forHEAD
template: content
BASE=$(git merge-base @ @{u} 2>/dev/null || echo "none")
-
the
@
is short forHEAD
-
@{u}
is short for@{upstream}
template: content
BASE=$(git merge-base @ @{u} 2>/dev/null || echo "none")
-
the
@
is short forHEAD
-
@{u}
is short for@{upstream}
Using that in scripting is a terrible idea, IMHO.
template: content
Git can be tricky enough as is
--
Make scripts reader-friendly where possible
--
-BASE=$(git merge-base @ @{u} 2>/dev/null || echo "none")
+BASE=$(git merge-base HEAD @{upstream} 2>/dev/null || echo "none")
template: section
.mega-octicon.octicon-circuit-board[]
template: content
- add branch metadata
template: content
-
add branch metadata
-
using exclude patterns in scripts
template: content
--
How:
git branch --edit-description <branch>
--
Data written to .git/config
--
- follows renames
- deleted when
<branch>
deleted
template: content
.git/config
meant to be extensible for third party uses
template: content
template: content
Ex: testing scripts that respect contents of:
-
.gitignore
-
.git/info/exclude
template: content
Re-implementing Git exclude patterns non-trivial
template: content
Re-implementing Git exclude patterns non-trivial
Essential plumbing command:
git check-ignore <pathname>
--
Returns <pathname>
if it matches an exclude pattern
template: content
git check-ignore <pathname>
template: content
git check-ignore <pathname>
- no recursive option for wildcard searches
--
# assume .gitignore includes the pattern: *sample.py
> git check-ignore plugins/*
# output would include
# plugins/basic_sample.py
# output would not include
# plugins/operators/basic_sample.py
template: content
git check-ignore <pathname>
-
no recursive option for wildcard searches
-
can't filter against custom ignore file
template: content
git check-ignore <pathname>
-
no recursive option for wildcard searches
-
can't filter against custom ignore file
-
not listed on
git-scm.com/docs
(but it's there)
template: content
First and third issues can solved:
--
git ls-files --others <pathname(s)>
--
Shows untracked files
template: content
Ex: pre-commit hook to do Python linting
--
# in .git/hooks/pre-commit
python_dirs= #...
pep8 \
--exclude $(git ls-files --others $python_dirs | tr '\n', ',') \
$python_dirs
template: section
.mega-octicon.octicon-circuit-board[]
template: content
2 separate repos, each with history
--
Import <subdir>
from <source-repo>
into <target-repo>
--
(and preserve history of <subdir>
)
template: content
# before
.<source-repo> .<target-repo>
|-- src |-- src
|-- bin |-- bin
|-- <subdir>
# after
.<source-repo> .<target-repo>
|-- src |-- src
|-- bin |-- bin
|-- <subdir>
template: content
- a one-time operation -> submodules inappropriate
template: content
-
a one-time operation, so submodules inappropriate
- e.g. split apart a large project
template: content
-
a one-time operation -> submodules inappropriate
- e.g. split apart large project
-
assume
<subdir>
does not exist in<target-repo>
, and that we want to use the same name
template: content
Step 1: construct a "synthetic" history
--
(all commits in <source-repo>
that touch files in <subdir>
)
--
In <source-repo>
:
git subtree split --branch <branch> --prefix <subdir>
template: content
git subtree split --branch <branch> --prefix <subdir>
template: content
git subtree split --branch <branch> --prefix <subdir>
- creates a new branch
template: content
git subtree split --branch <branch> --prefix <subdir>
- creates a new branch
- synthesizes commit history
template: content
git subtree split --branch <branch> --prefix <subdir>
- creates a new branch
- synthesizes commit history
- each commit has contents of
<subdir>
at root of project - repeated splits produce identical histories
- each commit has contents of
template: content
Step 2: get contents of <subdir>
into <target-repo>
:
--
In <target-repo>
:
git subtree add --prefix <subdir> <source-repo> <branch>
template: content
git subtree add --prefix <subdir> <source-repo> <branch>
--
- imports synthetic history, puts contents into
<subdir>
template: content
Done...with some caveats
--
git log
may not work as you expect
template: content
Look at the history of a given<file>
in <subdir>
:
--
In <target-repo>
:
>>> git log -- <subdir>/<file>
commit 2e259f35fea13ca6bbfb8bd33be6476caea5cc86
Merge: e6a4851 d0e7410
Add '<subdir>' from commit 'd0e74107e4517e48888b3de5bd7b129bc12a041f'
git-subtree-dir: <subdir>
git-subtree-mainline: e6a4851ca13c9fa76fd25277b1d1bb6cd7e61519
git-subtree-split: d0e74107e4517e48888b3de5bd7b129bc12a041f
>>>
--
Only 1 commit—didn't we import the full history?
template: content
Adding --follow
doesn't help
--
There's a workaround:
--
git log -- *<file>
--
But limitations to this
template: content
In <target-repo>
:
--
git checkout -b <branch>
template: content
In <target-repo>
:
git checkout -b <branch>
git remote add -f <remote-name> <source-repo>
template: content
In <target-repo>
:
git checkout -b <branch>
git remote add -f <remote-name> <source-repo>
git merge --allow-unrelated-histories <remote-name>/<branch>
--
git log -- <pathname>
should now work as expected
template: content
filter-branch
generalizes better (to processing all branches)...
--
... but it is notoriously tricky to use
--
subtree
simpler...
--
... but it lives in /contrib
(while filter-branch
ships with Git)
template: content
template: content
git checkout --orphan
to create new branch w/o parents, disconnected from all history
template: content
-
git checkout --orphan
to create new branch w/o parents, disconnected from all history -
for an abbreviated history, try
filter-branch
with grafts
template: content
- Git doesn't have any notion of "this commit was made on this branch"
template: content
- Git doesn't have any notion of "this commit was made on this branch"
- Git won't expand built-in commands used as aliases
- was debated, a case of Git not wanting to let people hurt themselves(!)
template: content
- Git doesn't have any notion of "this commit was made on this branch"
- Git won't expand built-in commands used as aliases
- was debated, a case of Git not wanting to let people hurt themselves(!)
- consider
git-sh-setup.sh
"scriplet"
template: content
- Git doesn't have any notion of "this commit was made on this branch"
- Git won't expand built-in commands used as aliases
- was debated, a case of Git not wanting to let people hurt themselves(!)
- consider
git-sh-setup.sh
"scriplet" - contribute to Git:
submitgit.herokuapp.com
template: content
- Git doesn't have any notion of "this commit was made on this branch"
- Git won't expand built-in commands used as aliases
- was debated, a case of Git not wanting to let people hurt themselves(!)
- consider
git-sh-setup.sh
"scriplet" - contribute to Git:
submitgit.herokuapp.com
- reduce merge conflict pain:
github.com/mhagger/git-imerge
template: title
.mega-octicon.octicon-circuit-board[]
Patrick McKenna