Useful aliases
There are a ton of those. Come up with your own, share them. Here are just a few useful ones to get started:
git config --global alias.tree "log --oneline --graph --decorate --all"
This modifies your git configuration and adds a "tree" alias. Trygit tree
from an existing repo.git config --global alias.listtags '!'"git for-each-ref --sort=subject refs/tags | awk '{print "'$2, $3'"}' | sed 's,tag refs/tags/, * ,' | sed 's,commit refs/tags/, ,'"
This will create an alias called listtags which will list all tags and mark annotated tags with an asterisk ( * ). Trygit listtags
from an existing repo.If you messed up with this or any other alias, you can easily remove a faulty alias with
git config --global --unset alias.myFaultyAlias
Deleting and renaming references
I've been tempted to put the notes below in the Git administration page of the wiki. I've left them here, because they might be useful for all, and that'll hopefully give me less work. However, I want to point out that a good understanding of Git would be preferable before attempting this, and more importantly, a good dose of thought given to the question: "do I really need to do this?". It's likely we'll do some of this, especially as we slowly starting using/getting used to Git, and right after our Subversion migration. But it should probably not be a regular task otherwise.
Deleting branches
git branch -d <branch-name>
will not be enough to completely delete a branch; it will only delete it locally. To delete the branch on the remote, git push --all
is not enough either. As of Git 1.7, you have to options: git push origin :<branch-name>
("take 'nothing' and make it <branch-name>") or git push origin --delete <branch-name>
(where 'origin' is the name of your remote, as usual)
Be again careful with possibly ambiguous names: in case there's also a tag with the same name you'll have to prefix <branch-name> by refs/heads/.
You still need to delete the branch locally, otherwise it will be pushed again next time you push --all
.
Any other existing clone will need to use the
--prune
flag for their next fetch
or pull
operation to become aware of the remote deletion; so make sure you only do this if really needed, and make sure all other users of this repository are notified.
http://stackoverflow.com/questions/2003505/how-do-i-delete-a-git-branch-both-locally-and-in-github
Deleting tags
The same applies as for branches. First delete the tag locally: git tag -d <tag-name>
then remotely with either git push origin :refs/tags/<tag-name>
or git push origin --delete refs/tags/<tag-name>
. Be again careful with possibly ambiguous names; <tag-name>
in the commands above should work just as well as refs/tags/<tag-name>
as long as there isn't a branch with the same name as the tag.
Like for branches, any other existing clone will need to use the
--prune
flag for their next fetch
or pull
operation to become aware of the remote deletion. Since we're talking about tags, the command will in fact have to be git (fetch|pull) --tags --prune
(by default, fetch only tells the remote to "send tags that point to things you're downloading", which is why a remove tag isn't sent in by default, same thing for a branch)
Things might start to break badly if you are using more than one remote.
Renaming branches
Assuming you've read the above, the following should start to make sense:
git push origin <old-branch-name>:<new-branch-name> :<old-branch-name> && \ git branch -d <old-branch-name>
... and ask other cloners to do git pull --prune
If you encounter some sort of issue with your srcspec or dstspec which "matches more than one" or "matches none", then the following could help:
git push origin refs/remotes/origin/<old-branch-name>:refs/heads/<new-branch-name> :refs/heads/<old-branch-name>
This executes the same command but with explicit references, and expects you don't have a local tracking branch of the same name, which is why we don't go git branch -d
afterwards.
always check the status after these commands with
git branch -a
from your clone, on the server, and from another clone for good measure.
Renaming tags
We're starting to get this aren't we ?
git push origin <old-tag>:<new-tag> :<old-tag> && git tag -d <old-tag>
... and ask other cloners to do git pull --prune --tags
Create a new branch from a tag
git checkout <tag-name> git checkout -b <new-branch-name> mvn versions:set -DnewVersion=<new-version-number-SNAPSHOT> git push origin <new-branch-name>
Reverting
The below apply mostly to reverting stuff that's already been pushed.
If you haven't pushed yet, a single-commit revert might be useful (but don't forget to rebase/squash before you push 2 irrelevant commits), but you'll probably just want to learn about git reset
instead.
Reverting one specific commit
That's easy. Identify the commit by hash or tree-ish.
git revert <the-hash>
This creates another commit on top of the current HEAD which is an exact inverse of <the-hash>
. If that commit can't be applied, you'll have to resolve merge conflicts: edit, git add ...
to accept your resolution, then git revert --continue
.
The automatically generated commit message says "revert <whatever the other commit said>" and contains the hash of the commit in question. Feel free to edit it to add more relevant information !
Note: you can pass several hashes to the command, and that will create as many revert commits as hashes you passed.
Reverting a range of commits
You shouldn't have merged. Yikes. Something goes wrong. Panic mode, revert all of the things !!1!
You could use git revert
for each commit, and locally git rebase -i
and squash
before pushing the reverts.
git revert OLDER_COMMIT^..NEWER_COMMIT
Where OLDER_COMMIT
is the oldest of the commits you want to revert (inclusive) and NEWER_COMMIT
is.. you guessed it.
This also generates N revert commits, thus will ask you N times to confirm the revert-commit message. Rebase and squash if necessary before pushing.
For the brave only
There are other techniques. They might break things. They might upset your colleagues. They might create more mess than you wanted.
I've tested some of these on testing.git
with one file of 5 lines. It works well. You're on your own if you want to do this on a 80K sloc project.
See http://stackoverflow.com/questions/4716051/rollback-to-an-old-commit-using-revert-multiple-times
Extracting submodules (preserving history)
Convert submodule to proper repo
git clone https://git.magnolia-cms.com/magnolia_main some-temp-copy cd some-temp-copy # If the submodule has already been removed, go back in history: git log -10 ## figuring out how far back to go to recover the modules we've already removed from master by now. 5511ebe in this case. git reset --hard 5511ebe # checkout and track all branches. warnings can be ignored. git branch -r | sed 's;^ origin/;;' | grep -vF "HEAD -> " | xargs -L 1 -I {} git branch --track {} origin/{} # make sure we have all branches in "local" now: git branch -a # remove the remote, so we won't accidentally push anything in the wrong repo. git remote -v git remote rm origin git remote -v # Actual filter-branch. Added --prune-empty (not sure if necessary, I think it's the default for --subdir-filter) and --tag-name-filter, which allows renaming tags in the same go -- in this case, it replaces any occurence of "magnolia-" by "mail-", simply using sed. # note that the original tags (magnolia-...) are kept, so you'll need to delete them ! git filter-branch --tag-name-filter 'sed s/magnolia-/mail-/' --prune-empty --subdirectory-filter magnolia-module-mail -- --all # reconfirm we still have all branches. # todo: checkout branches and check their contents git branch # same for tags. here we see they've been renamed git tag # Clean up and shrink repo. note: this won't have a LOT of effect on the size, until you delete irrelevant branches and tags (especially those that are from before the module was created, because those contain the whole project). # You can use the following little script to locally bulk delete all the original tags starting with 'magnolia'. As usual, double check it before running it! ## for t in `git tag | grep ^magnolia-*` ; do git tag -d $t; done; # Remove other irrelevant branches and tags # Clean and shrink git reset --hard git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d git reflog expire --expire=now --all du -hd1 #before git gc --aggressive --prune=now du -hd1 #after # now push ! we're not adding a remote, to avoid confusion. this repo here is really just a temporary one. drop it and clone from the server afterwards ! git push --all https://git.magnolia-cms.com/modules/mail git push --tags https://git.magnolia-cms.com/modules/mail
See stackoverflow for more details.
Default ignores for new modules
We want to ignore the same resources we ignored for svn (especially all eclipse & intellij stuff).
In order to do so with git, simply add the following lines to the .gitignore file in the root of your git repository
target *.iml *.ipr *.iws *.log .idea .project .classpath .settings .checkstyle .DS_Store rebel.xml
Varia
- View remote branch tracking (which local branch tracks which remote branch):
git branch -vv
(-vv
is for super verbose) or,git remote show origin
Good commit messages
Unless it opposes to Magnolia guidelines and as long as consistency is not broken the following tips (source) are good enough to consider:
- Begin your message with a short summary of your changes (up to 50 characters as a guideline). Separate it from the following body by including a blankline.
- The body of your message should provide detailed answers to the following questions:
- What was the motivation for the change?
- How does it differ from the previous implementation?
- Use the imperative, present tense («change», not «changed», «changes» or «changing») to be consistent with generated messages from commands like git merge.
More, more, moreeeee !
- Matthew McCullough's Recommended Git Links: https://bitly.com/bundles/matthewmccullough/1
- Matthew McCullough's .gitconfig files (contains A LOT of aliases!): https://github.com/matthewmccullough/dotfiles/blob/master/gitconfig
2 Comments
Magnolia International
Natascha, super thanks for the cool alias - would you mind sharing the whole command (
git config ...
so one can easily add it without fiddling with the config file ? From there on, it might be a little over-the-top to have the example complete on this page, which tends to clutter the page a little ?Natascha Desmarais
Just for everyone else - as we discussed this - obviously you're right. I will remove the extra infos that nobody really needs
. Once I figure out how to make bash accept the "!" as part of the string and not interpret it, I will repost the command properly.
Edit: Seems like bash doesn't ignore exclamation marks and dollar signs in double quotes but only in single quotes. Instead you can concatenate double and single quoted strings simply by ommitting the space between them
echo "foo"'bar'
=>foobar
. @Everyone: Feel free to make thelisttags
alias a little nicer if you figure out how.