Git Hacks
Use git commit
with an editor in WSL
When I git commit in wsl, with code editor set to…
git config.editor --global "code"
…it says Aborting commit due to empty commit message
You must set it to:
git config.editor --global "code --wait"
Move already committed files to lfs
git lfs migrate import --include="*.pdf,*.mp3" --verbose
Clone a remote branch
git clone --branch repost --single-branch git@github.com:USNC-Power/REPOST.git -- repost-develop
Download only 1 git folder
If you just want the data without cloning the repo you normally have the option of using GitZip.
It will not work with git LFS.
Clone without checking out any files
Commit git to remote repository without cloning any local repository
You can clone the repo with -n
to not checkout any files.
--depth 1
will truncate the history of each file to their last commit
git clone -n git://path/to/repo.git --depth 1
cd repo
Checkout only the files you need to change for your commit
git checkout HEAD file.ext
Just be careful to only commit the file you’re working on. If you do something like git add .
you will be committing the deletion of all files you have not checked out!
Submodules
git submodule add <ssh-url>
All this does is add the .gitmodules
file and update the git config.
Add a specific branch as a submodule if you want to do this, it is easiest to just edit the .gitmodules
file to add the branch = <>
keyword
[submodule "blala"]
path = blala
url = git@github.com:seb/blala.git
branch = dev
[submodule "nice"]
path = nice
url = git@github.com:seb/nice.git
branch = main
You can’t add submodules as references to branches within the same repo
Update the git config file:
git submodule init
Update the submodules to the most recent remote commit:
git submodule update
To remove just run:
git rm <path-to-submodule>
and commit.
Editing within submodules
You have the make the adds and commits within each submodules, you then have to commit those commits up a level in the master repo. You can push all from the master repo.
When you pull them on another machine, you have to first:
git submodule update --init --remote
git pull
If you have actually changed branches of any of the submodules run this to switch them to the remote setting:
git submodule update --remote
If there are conflicts you might have to first reset your whole repo. If you specify a branch here, it will move all the submodules to main. Not a good idea
git reset --hard --recurse-submodules
GUIs
-
GitExtensions, launch from command line with
gitex
. Lets you configure literally everything, but it is a bit clunky. -
GitHub Desktop, Launch from command line with
github
. Cant configure bare repositories.
Use the VSCode built-in source control tab. Click on the git-graph extension. Can’t configure the repo from here. ***
Worktrees
Git Worktrees are ways to have multiple branches of a repo checked out at once in separate folder structures without having to clone the entire repo twice.
First make a ‘bare’ repo. This is basically just what would normally be in the .git
file, but in the top level of the directory:
git clone --bare <path>
Then add a worktree by checking out the existing branch:
git fetch origin refs/heads/*:refs/heads/*'
git workree add <branch name> <branch name>
GitHub CLI
Use the GitHub Command Line Interface to create and delete repositories
I installed ‘GH CLI’ via:
winget install GitHub.cli
Check which account you are logged in to with:
gh auth status
Login with:
gh auth login
Create a remote repo from an existing repository:
gh repo create <repo-name> --private --source=. --remote=upstream
Or create one interactively with the following to handle any situation:
gh repo create
If you are pushing for the first time and you’re getting the error: fatal: the requested upstream branch 'origin/master' does not exist
, you may need to use:
git push -u origin main
.gitignore everything except
If you want to not-ignore both a folder and its contents, you have to use the convention:
*
!decart2d_view/
!decart2d_view/*
Git LFS
Use git lfs to store files over 50MB on your repo. You have to install a separate program.
Make sure you have git lfs installed otherwise you will only get templated empty files.
Then go to the root of the repo and run the following to make the repo use git lfs:
$ git lfs install
> Git LFS initialized.
If you are on windows also make sure you run before cloning: to ‘checkout as-is, commit unix style line-endings’. Otherwise git may change millions of line endings in the lfs file without you asking it to.
git config --global core.autocrlf input
Track your first file. This will create a .gitattributes file in the top level of the repo:
git lfs track blabla
You can then edit this file to include and exclude directories. For example using the same convention as .gitignore with ‘everything except’ for example:
# Path GLOB On files On Diffs On merges
* filter=lfs diff=lfs merge=lfs -text
.gitignore filter diff merge text eol=lf
.gitattributes filter diff merge text eol=lf
decart2d_view/README.md filter diff merge text eol=lf
Subsequent lines override prior lines like in .gitignore
attribute=value
mean Set to value
-attribute
means unset
attribute=
or attribute
means Set to the default value
` ` not present means ‘unspecified’
Therefore for this says that for every file in the repo (*
), apply the lfs filter (actually a file operation), use lfs for diffs and for merges. Unset text attribute means git does not attempt end of line conversion on these files on checkin or checkout. No EOL specified as it has no effect unless text attribute is set.
* filter=lfs diff=lfs merge=lfs -text
And for this file, overridding the previous line, set the filter, diff and merge back to default and add text which adds eol normalisation back on checkin and checkout, eol then makes this lf
.gitignore filter diff merge text eol=lf
What each of these attributes does to the path in question can be found at git-scm
How can I tell if a file will be uploaded to git lfs correctly?
f everything is set up correctly, you can verify that git LFS is going to work properly by:
git add
the file(s) in question. You must rungit add
to refresh the state of files- Do one of the following:
- Run
git lfs status
and ensure the file(s) in question appear underGit LFS objects to be committed
, and that they have theLFS
value in parenthesis; or - Run
git lfs ls-files
and ensure the file(s) in question appear in this output.
- Run
Use PowerShell Aliases
I use a number of aliases in my PowerShell Profile:
set-Alias -Name g -Value git
function gd { git diff $args }
function s { git status $args}
function l { & git log $args }
function rl { & git reflog $args }
function lo { git fetch; git branch --show-current | git log }
function rlo { git fetch; git branch --show-current| git reflog }
Function c {git commit -am "$args"}
Function gcp {git commit -am "$args"; git push}
function a { git add $args }
function aa { ga -A}
function gb { git branch $args }
function gs { & git switch $args }
Function pl {git pull $args}
Function p {git push $args}
function gt { & git log --graph --oneline --decorate $args }
function gr { & git remote -v $args}
function gf { git fetch }
function gd { git diff $args }
Clone specific parts of a git repo
Install from remote using pip:
pip install git+ssh://git@github.com/user/repo
Install from a specific branch, version tag or commit ref: @ref
pip install git+ssh://git@github.com/user/repo@ref
Or install from a specific directory: #subdirectory=dir
pip install git+ssh://git@github.com/user/repo#subdirectory=dir
Or install a specific package name:
Or install from a specific branch and directory and package :@branch#subdirectory=path#egg=package
pip install git+ssh://git@github.com/user/repo@branch#subdirectory=path#egg=package
Download Specific parts of a repo
Use conventions as above:
git clone git@github.com:user/repo@branch#subdirectory=path
You have to use git archive to get a specific file. It always downloads it as a compressed format like a tar so have to extract it out again.
def get_file_from_remote(git_url, fn, branch ):
''' Get a specific file fn from remote repo ssh url i.e. git@github.com/user/repo
yes it should be / rather than : as this uses the git+ssh git protocol to clone the file.
Saves the file to local directory'''
ssh_url = "git+ssh://"+git_url
with open(fn + '.tar', 'w') as f_tar:
subprocess.run(
['git', 'archive', '--remote=' + ssh_url, '--format=tar', branch,
fn], stdout=f_tar, stderr=subprocess.STDOUT, check=True)
with tarfile.open(fn + '.tar', 'r') as tar:
# Extract the contents of the tarfile
f = tar.extractfile(fn).read().decode()
with open(fn, 'w') as g:
g.write(f)
or a single command line in bash:
git archive --remote=ssh://host/pathto/repo.git HEAD README.md | tar xO
Can’t see remote branches
This command will list all the branches that exist on the remote repository, regardless of whether they are tracked or not.
git ls-remote --heads
git remote set-branches --add origin <remote branch>