Gitflow and forward slashes - linux

I'm looking at starting to use Gitflow. I notice that by default the branches end in forward slashes.
I.e.
hotfixes/
releases/
Etc.
Does the forward slash cause any problems as git uses a forward slash as a folder delimiter.
Will it cause any problems with deployment from git with the forward slashes in it?
Could we use another prefix such as hyphen?
What are the pros and cons of either method.
Thanks in advance

I'm going to re-order these questions a bit.
Could we use another prefix such as hyphen?
You can use any name format you want, as long as you respect the limitations of your own OS (operating system—Windows, MacOS, Linux, and so on). Using a hyphen does not fix the limitations.
Does the forward slash cause any problems as git uses a forward slash as a folder delimiter.
Git doesn't use it this way; it's your OS that does. The file path name a/b means "find name b within directory a", even on Windows.
Will it cause any problems with deployment from git with the forward slashes in it?
Not if you avoid one your OS's limitations. Let's take a quick look at those.
Background
Git will, at various times, write branch names (or any other reference names) into a path rooted in the .git/refs/ directory. The "full name" of any branch B is refs/heads/B, e.g., refs/heads/master, refs/heads/develop, refs/heads/feature/tall, refs/heads/bugs/short. These will wind up creating files named .git/refs/heads/master, .git/refs/heads/feature/tall, and so on. In each of these files, Git stores a simple value: the hash ID of the target object (a commit hash, for branch names).
At other times, Git will write all these names into a single "flat file": essentially, a really poor substitute for a key-value store database. Whenever Git uses its own database, rather than the operating system's file system, Git avoids the OS's limitations. But since this is not all the time, you must respect your OS's limitations.
A directory (or "folder") is never a file, and vice versa
Suppose you have a branch named x. This means Git sometimes creates a file named .git/refs/heads/x to hold the tip commit hash ID for x.
If you now try to create a branch named x/y, Git may need to create a file named .git/refs/heads/x/y to hold the tip commit hash ID for x/y. But there is already a file named x, so it becomes impossible to create a directory named x holding a fie named y.
This means you must avoid using one name that becomes a prefix of the other when / is treated as a path name separator.
(This same rule applies to the names of remotes. You may, if you like, name two different remotes hq/a and hq/b, but if you do that, you must not name any remote hq.)
Case folding
Suppose your OS folds case. That is, suppose that you create a file named ReadMe.txt and then ask to open or view or create-or-write a file named readme.txt. Does this make a new file readme.txt that is different from the existing ReadMe.txt, or does it re-use the existing ReadMe.txt? If your OS re-uses the existing ReadMe.txt, your system folds case: it retains the uppercase and lowercase mix you use when you first create a file or directory, but from then on, use of any name that resembles the original name, but has a different mix of upper- and lower-case letters, will wind up re-using the original case.
That means that if you create a branch named master, and then try to create another branch named MASTER, your OS will re-use the lowercase name. This will, in a sense, "confuse" Git: Git thinks that master and MASTER are different branches, but your OS insists that the two different branch values be stored in a single file.
The same applies to directory names within the path. If you name one branch feature/tall and a second branch Feature/wide, Git will think that the second branch should always be named Feature/wide—but when Git goes to create a new file .git/refs/heads/Feature/wide, your OS says to itself: "Aha, I should re-use the existing lowercase-f feature directory and create this as .git/refs/heads/feature/wide." This once again confuses Git.
When this situation occurs, the behavior is somewhat hard to predict. If your OS does case folding on files,1 you should avoid the situation entirely. A good practice here is to pick one case—usually lower-case only—and stick with that.
1Modern OSes, including Linux and MacOS, actually choose whether to do case-folding on a per-file-system basis. This means you can set up a file system that is fully case-sensitive—file readme.txt is entirely different from file ReadMe.txt—and use that, to work around problems with branch names. However, Linux defaults to fully case sensitive for all its native file systems anyway.
Unicode normalization
Case folding, as performed by operating systems, tends to be limited to ASCII: they may not have a concept of an uppercase é, for instance. This is especially true if they use Unicode to represent "non-ASCII" characters (accented characters, Arabic, Chinese, Cyrillic, Hangul, Hebrew lettering, and so on). But if the OS supports Unicode at all for these wider character sets, it often uses UTF-8 encoding,2 which "hides" the Unicode encoding from the OS-level operations, which treat path names as strings-of-bytes. The "higher order" non-ASCII characters, even encoded, never clash with the ordinary ASCII characters such as /, so that when the OS uses / to choose to make or use a directory, this does not affect the non-ASCII characters.
The process of Unicode normalization is a bit mysterious to those who have not experienced it. See the Wikipedia page on Unicode equivalence for details, but in short, consider the idea that ü might be expressed as a single character "u with umlaut", or as the combining sequence "umlaut, u". If the OS wants to fold case, it probably should also treat these two distinct byte-sequences as naming the same file.
MacOS performs Unicode normalization to deal with this (NFD in HFS+; see this StackOverflow posting and its various links). This too affects Git (which, since Git version 2.1, has code to deal with it, though there was a bug fix as late as Git 2.8.4). I have no experience with it, but in general I would just advise avoiding the problem: stick with simple ASCII branch (and other reference) names.
2Windows uses UTF-16-LE, but the effect is similar.

The only problem I encountered is that if you have master branch you cannot create a branch named master/foo/bar because it starts with an already branch name.
I use [fix/feature|path]/{issue-number}/[description/]{destination} pattern
examples:
fix/42/master tells that this branch/pr is a fix of issue number 42 on master branch
feature/23/1.4 tells that this is a feature 23, for next minor release 1.4
and again
feature/123/create-red-button/master add a red button (the card number 123) on master branch
and so on

Currently you can set a different suffix when initialize gitflow with git flow init like this:
❯ git flow init
No branches exist yet. Base branches must be created now.
Branch name for production releases: [master]
Branch name for "next release" development: [develop]
How to name your supporting branch prefixes?
Feature branches? [feature/] feature-
Release branches? [release/] release-
Hotfix branches? [hotfix/] hotfix-
Support branches? [support/] support-
Version tag prefix? [] v
As you can see, in brackets are the default values (forward slash as suffix), but you can overwrite it with yourbranchname- (dash at the end)

Related

Is there a way to still use symbolic link for .gitignore? And why it's not supported anymore as a symbolic link?

I have a central .gitignore on my laptop and for each project that I create, I create a symbolic link to that central file so that I can keep a uniform policy across all of my projects.
All of my projects are like each other (technology-wise) and it makes sense to have a central .gitignore to reduce the burden of maintenance.
However, recently I see this message:
warning: unable to access '.gitignore': Too many levels of symbolic links
And as I searched, it seems that from git 2.3 upwards they have decided to not support the symbolic link.
I have two questions. First, is there a way to force git to support symbolic links for .gitignore? And why on Earth do they not support it anymore? Does it not make sense to reuse code? Is half of linux not reused through symbolic links?
First, is there a way to force git to support symbolic links for .gitignore?
No.
And why on Earth do they not support it anymore?
The gitattributes documentation now (as of Git 2.32) says this near the end:
NOTES
Git does not follow symbolic links when accessing a .gitattributes file in the working tree. This keeps behavior consistent when the file is accessed from the index or a tree versus from the filesystem.
While I'm not 100% sold on the reasoning here myself, it does make sense. (It seems to me that Git could just stuff the content of the .gitattributes file into the index and hence into the commits, although this would mean that on checkout it would destroy the symlink.)
Optional further reading / background
First, let's describe what a "symbolic link" is in the first place. To do this we must define what a file is (which is a pretty big job, so we'll just do very light bit of coverage): A file is a named entity, typically found in a file system (systematic collection of files), that store data for later retrieval. Being a named entity, a file has a name: for instance, README.txt, Makefile, and .gitconfig are all file names. Different OSes place different constraints on file names (e.g., Windows refuses to store a colon : character in a file name or create any file named aux with or without a suffix, so that you cannot have a C or C++ include file named aux.h or aux.hpp). Git itself places very few constraints on file names: they can contain almost any character except an ASCII NUL (b'\0' in Python, \0 in C, etc.), and forward slashes / are slightly special, but other than that a name character is just a name character and there are very few restrictions.1
On most real OSes, files can have "types". The exact mechanisms here rapidly become OS-specific and can get very complicated,2 though traditional Unix-like hierarchical file systems just have a few types: "directory", "file", "block or character device", "symbolic link", and the like. Symbolic links are in fact one of these types.
A symbolic link is a type of file in which the file's content is another file name. This file name, on a Unix-like file system, can be absolute (/home/john/somefile, /Users/torek/somefile) or relative (./somefile, ../../somefile). On these systems, opening a symbolic link results in opening the file whose name is provided by the symbolic link's content. To read the content of the symbolic link—that is, to find out what file name the link contains—we use a different operation: readlink instead of open, for instance. Modern Unix systems also have an O_NOFOLLOW flag that can be used to forbid the open system call from following the link.3
The way Git stores a symlink is as a special mode object in a commit: ordinary files are either mode 100644, meaning a non-executable file, or mode 100755, meaning an executable file. A symbolic link is stored as mode 120000 and Git stores the target name, found by calling readlink, as the content.4
1The one peculiar restriction is that you're not allowed to store anything named .git, in any mix or upper and/or lower case. This .git restriction actually applies to "name components" which are the parts between forward slashes. Due to Windows being Windows, Git-on-Windows will turn backwards slashes into forwards ones as necessary, and then places the restriction on the components.
2Traditional OSes from the 1960s through 1980s, for instance, may impose things called access methods based in part on file types. Unix simplified things a lot here.
3This is sometimes important for various security aspects. The details are beyond the scope of this article.
4These odd mode values correspond closely to the struct stat st_mode field in a Unix/Linux stat system call. That's because when Linus Torvalds first wrote the initial versions of Git, he was dealing with it—at least in part—as a kind of file system. The ability to store full Unix file modes (9 bits of rwxrwxrwx flags) was left in, and initially Git actually stored group write permissions, but this turned out to be a mistake and was removed before the first public release. The 100000 part is S_IFREG, "Stat: Inode Format REGular file". The 120000 found in a Git symbolic link is S_IFLNK, or "Stat: Inode Format symbolic LiNK". We also have mode 040000 for directories from S_IFDIR, which should now be obvious. However, Git can't store a mode 040000 entry in its index / staging-area, for no particularly good reason, which leads to the problem described in How can I add a blank directory to a Git repository?
In other words, a symbolic link means "use another file"
Wherever a symbolic link is found, it means read or write some other file. So if README.txt is a symbolic link reading /tmp/fooledyou, any attempt to read README.txt actually reads /tmp/fooledyou instead; any attempt to write README.txt actually writes to /tmp/fooledyou.
Consider, though, when this redirection—from README.txt to /tmp/fooledyou—occurs. It doesn't happen at the time you make the symbolic link itself. You can create this README.txt file last year. When I go to read README.txt, that's when the redirection occurs. So if you've changed /tmp/fooledyou since you created README.txt, I get the modern version, not the old one.
That, of course, is precisely why you wanted the symbolic link in the first place:
All of my projects are like each other (technology-wise) and it makes sense to have a central .gitignore to reduce the burden of maintenance.
In other words, you wanted to have one .gitignore, that is not version controlled, that always reflects what should be ignored based on what you learned up until right now, regardless of when it is that "right now" is.
This is the opposite of Git's normal purpose, which is to store a full snapshot of what your project looked like "back then", whenever "back then" was: the time at which you made a git commit snapshot.
My suggested possibility above is that when you run:
git add .gitignore
to update Git's idea of what should go in the .gitignore file that goes in the next commit, Git could follow the .gitignore indirection at that time, read the contents of the target of the symbolic link, and prepare that to be committed. You'd then make the commit—the snapshot and metadata—such that if, next year, you extract this particular historical commit, you'll get the historical snapshot, including the historical .gitignore.
The drawback to this is that by extracting the historical .gitignore, you "break the link": .gitignore is no longer a symbolic link at all. Instead, it is now an ordinary file, containing the historical snapshot. There's no way to get the link back except to remove the ordinary file and create a new symbolic link.
Before Git version 2.32, Git would notice when .gitignore was a symbolic link and would store, in its index / staging-area, the fact that .giginore was a symlink (mode 120000) and use the readlink system call to find the target of the symlink, and store that in the commit. Running git commit then makes a snapshot that, when extracted, creates .gitignore as a (new) symbolic link: the existing file-or-symlink is removed, and the new one is installed instead. It redirects, in the usual symlink fashion, to the saved (committed) historical location—even if that's wrong now.
As of Git version 2.32, Git will still store a symbolic link .gitignore file:
$ mkdir z; cd z
$ ../git --version
git version 2.36.1.363.g9c897eef06
$ ../git init
[messages snipped; branch renamed to main, also snipped]
$ echo testing > README
$ ln -s foo .gitignore
$ git add README .gitignore
$ git commit -m initial
[main (root-commit) 08c6626] initial
2 files changed, 2 insertions(+)
create mode 120000 .gitignore
create mode 100644 README
$ ../git ls-tree HEAD
120000 blob 19102815663d23f8b75a47e7a01965dcdc96468c .gitignore
100644 blob 038d718da6a1ebbc6a7780a96ed75a70cc2ad6e2 README
The same reasoning—that a Git commit, once it's made and stuffed into a repository, may contain a symbolic link that is no longer valid or correct—explains why Git 2.32 also refuses to follow .gitattributes and .mailmap files. Note that commands like git archive generally use the commit's version of .gitattributes to control archive substitutions, so a symbolic link stored in the repository is useless unless the target of the symbolic link is somehow correct. The repository and its commits get shipped around from one machine to another, but the targets of any committed symlinks in many cases don't.

p4 status detects identical files as reconcile to edit

In my workspace when I run p4 status, it marks a bunch of files as "reconcile to edit". However, if I reconcile them and do a diff, all these files are identical.
Does anyone have some idea on what would be the cause?
These files include png, js, php, ttf, xsl files.
I have my workspace configured with: "Allwrite" and "Line ending characters for text files: UNIX" (this is a Windows 2012 R2).
I was guessing the Line ending was the cause, but the files include png, and after I actually submit the changelist and do p4 status, it still report the same list of files (well, I notice a few files are gone, to there maybe something there for whatever reason).
My workspace is new, so the files really shouldn't have any changes.
One possible cause of this is that the stored checksums don't match the actual file content. As an admin you can run:
p4 verify -q files ...
to do a server-side comparison of files vs their stored checksums -- if you get "BAD!" results it means they don't match each other (which means they'll also always show up as "different" on client machines since the same checksum is used there). Checksums not matching depot revision contents can be a bad thing (a given revision is supposed to be immutable) and should prompt investigation since it might indicate hardware failure, tampering, et cetera. There may also be benign ways a revision's content can change so it no longer matches its checksum -- one example is if you have dynamically expanded $DateTime$ keywords in the file and you change the server's timezone.
If the content is fine and you want to update the checksum to match, do:
p4 verify -v file
It seems like there could be several reason for this behavior. #SamStafford answer may surely be one of them.
In my case, it turns out that those files has versions that differ by capitalization. Since Windows is case insensitive, that's what messed it up and always marked them as changed.
The OP mentioned "I was guessing the Line ending was the cause" in the question, but this was a little cryptic for me and I missed how this was in fact exactly what I happened to be looking for.
In related question & answer https://stackoverflow.com/a/49803790/74296, #Samwise explains that the workspace line ending configuration "Shared" can cause this when a file is stored in perforce as "Windows style" line endings.

Is there a way to swap files, and preserve history in perforce, in one step?

I have some files, like upgrade01.x and upgrade02.x, where the name defines some order. Due to external constraints, I must use this naming convention but I want so swap them, 01 become 02 and 02 become 01. I tried renaming update01.x to a temp name (without submitting it), then tried change update02.x to be update01.x but perforce doesn't let me do that.
I can submit the rename to something temporary, then rename one to the other, then the rename the temp, but is a monster pain. Also, the history looks really weird (but that might be unavoidable).
Is there a way to do this in one sane operation?
Doing the rename in two steps (rename both to intermediate names, then each back into the place of the other) might be the preferred option if you want the "identity" of each file to follow it to the new name (e.g. when you merge from other branches, you want merges to be remapped according to the new name rather than going to the old one).
If your priority is to have this happen in a single changelist, though, and you're not so concerned about whether the history preserves the notion of which file is which, I'd do it like this:
p4 copy upgrade01.x upgrade02.x
p4 copy upgrade02.x upgrade01.x
p4 submit
This will get you a nice neat little X in the revision graph (which I suspect is what you want), but not a true rename.
Merging to/from other branches will be a little tricky either way; if you go the true rename option, when you merge the rename to other branches you'll need to go change by change to repeat the process of renaming to something intermediate and then back (since the target branch will have the same constraint of not being able to rename onto something that is itself already open for rename). If you go the copy option, you'll need to manually specify which file to merge into which depending on what state each branch is in and whether there are outstanding changes requiring an actual merge.

How to realize the "cp -u" function in a clearcase vob?

We have two vobs which are "voba" and "vobb". And there is a directory "abc" in both vob and contains the same .h / .cpp files.
Usually, the files in "abc" dir in "voba" are updated quite frequently. And from time to time, I would like to update all files in "abc of vobb" from "abc of voba", which means:
Checkout the updated files in vobb.abc, overwrite them and then check in.
Copy the newly created files to vobb.abc, create element.
Delete the deleted files in vobb.abc by corrspoding to voba.abc.
If it is a common linux directory, I think cp -u and achieve that. But when it comes to the clearcase, I can only do the above 1-3 by hand.
Is there any easy way to finish that update automatically?
This is called in ClearCase a clearfsimport (potentially used with the -mirror option)
Since the elements in the directories abc of the two vobs are completely different (different oid, with different history), what you can do is import the content of abc from one vob into another: clearfsimport will automatically checkout, update and checkin only the files that have evolved in the source, and need to be updated in the destination.
Note, this recent thread (March 2013) also points out to the perl script ClearCase::SyncTree
It is superior to clearfsimport in many respects, especially in its evil twin avoidance (it will try with the proper options to link suitable entries from non visible versions).
Description:
This module provides an infrastructure for programs which want to synchronize a set of files, typically a subtree, with a similar destination subtree in VOB space. The enclosed synctree script is an example of such a program.
The source area may be in a VOB or may be a regular filesystem; the destination area must be in a VOB.
Methods are supplied for adding, subtracting, and modifying destination files so as to make that area look identical to the source.
Symbolic links are supported, even on Windows (of course in this case the source filesystem must support them, which is only likely in the event of an MVFS->MVFS transfer). Note that the text of the link is transported verbatim from source area to dest area; thus relative symlinks may no longer resolve in the destination.

Is it possible to create a branch from a tag in TortoiseSVN without first checking out the tag from SVN server?

Our trunk directory contains about 100mb of code and we create tags from the trunk directory. Normally, this is not an issue because a tag takes up no space until you need to use it for something. Since branches are created from tags in SVN, how can I create a branch from a tag wtihout first checking out the tag? It appears I need to do a Tortoise Update from Windows Explorer to get the tag down to my local machine before I can use Tortoise > Branch/Tag... to create a branch from it. This seems illogical since we don't make changes to tag folders, and it requires that I check out 100mb of code, only to create a branch, and then check out another 100mb of code in the branch folder, where the changes will actually be made.
Ideally, I'd be able to create a branch directly in the repository via RepoBrowser - but I can't see an option for it there.
Am I missing something?
Creating a branch is just a matter of copying the desired directory to another location. This can be done directly in the Repo Browser.
There is nothing special that differentiates a branch from a tag as far as Subversion is concerned. It is just a convention to store them under separate parent folders, named 'branches' and 'tags' accordingly.
The only difference between tags and branches is your attitude towards them -- they are equivalent behind the scenes -- shadow copies made in the repository.
You don't even need a working copy to create a branch/tag. For example, you can use this command-line:
svn copy http://server/test/trunk http://server/test/branch -m "make branch"
(assumes you have SVN.EXE in addition to TortoiseSVN, otherwise, use Ben's answer)

Resources