A compilation of not trivial Git tricks and hacks
This should do the trick and only add the specified subset of submodule files (just tweak .git/modules/<sub>/info/sparse-checkout
accordingly):
https://stackoverflow.com/questions/45688121/how-to-do-submodule-sparse-checkout-with-git
git clone --no-checkout path/to/sub sub # optional: --depth=1
git submodule add ./sub
git submodule absorbgitdirs
git -C sub config core.sparseCheckout true
echo 'foo/*' >> .git/modules/sub/info/sparse-checkout
git submodule update --force --checkout sub
N.B. Maybe newer versions of Git can achieve the above in a more simple way.
To rebase a branch upon the point it was born, e.g. develop
and squash all commits in that branch
(typical use case can be a shared branch with frequent pulls from develop), do the following:
git reset $(git merge-base origin/develop YOUR_BRANCH) # or just `develop` if updated with origin
git add -A
git commit
All the commits in YOUR_BRANCH
will be gone, leading to a single final commit on top of develop
.
N.B. Rebasing rewrites the history, so be sure the branch is no longer shared when you squash
it. Finally do a push --force
if the branch was already pushed.
Unset remote tracking branch: git branch --unset-upstream
git push --delete origin TAG
git tag -d TAG
Temporarily ignore changes:
During development it's convenient to stop tracking file changes to a file committed into your git repo. This is very convenient when customizing settings or configuration files that are part of your project source for your own work environment.
git update-index --assume-unchanged
Resume tracking files with:
git update-index --no-assume-unchanged
Permanently ignore changes to a file:
If a file is already tracked by Git, adding that file to .gitignore
is not enough to ignore changes to the file.
You also need to remove the information about the file from Git's index:
These steps will not delete the file from your system. They just tell Git to ignore future updates to the file.
- Add the file in your .gitignore.
-
git rm --cached FILE
- Commit
cd taret-repo
git --git-dir=../<some_other_repo>/.git \
format-patch -k -1 --stdout <commit SHA> | \
git am -3 -k
(-1
= single commit; -3
= 3-way merge)
(Option A) Using patch (if history is sane):
git log --pretty=email --patch-with-stat --reverse -- path/to/file_or_folder | (cd /path/to/new_repository && git am)
(Option B) Adding the source repo as temporary remote:
git remote add srcrepo https://example.link/source-repository.git
git fetch srcrepo
git merge srcrepo/srcbranch --allow-unrelated-histories
git remote rm srcrepo
And resolve potential conflicts.
(Option C) Fetching a repository branch:
git fetch https://example.link/source-repository.git master
gIt checkout master
git merge FETCH_HEAD
And resolve potential conflicts.
N.B. fetch
and merge
can be replaced by pull srcrepo srcbranch
This will allow you to migrate code from repository srcrepo
to repository dstrepo
while
preserving srcrepo
's history and authorship of files. This can be done into a specific prefix path
of destination repository instead of merging into the root, effectively allowing the merge of two
different projects together.
git remote add -f srcrepo https://example.link/source-repository.git
git merge -s ours --no-commit --allow-unrelated-histories srcrepo/master
git read-tree --prefix=path/to/destination -u srcrepo/master
git commit -m "Srcrepo merged into dstrepo"
git remote rm srcrepo
Notes (in command order):
-f
stands forfetch
, so add remote and fetch it.-s
ours makes it not apply the changes, as that would apply them to the root of the repository (not to a subdirectory).-u
makes it apply them not only to index, but also to the working directory.
More about it: