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. Try git 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 ( * ). Try git listtagsfrom 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.

(warning) 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.

(warning) 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)
(warning) 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> &amp;&amp; \
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.

(warning) 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> &amp;&amp; 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 !

  • No labels

2 Comments

  1. 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 ?

    1. Just for everyone else - as we discussed this - obviously you're right. I will remove the extra infos that nobody really needs (smile). 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 the listtags alias a little nicer if you figure out how.