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

Pro’s use git 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:

  1. git add the file(s) in question. You must run git add to refresh the state of files
  2. Do one of the following:
    • Run git lfs status and ensure the file(s) in question appear under Git LFS objects to be committed, and that they have the LFS value in parenthesis; or
    • Run git lfs ls-files and ensure the file(s) in question appear in this output.

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>